Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/azatoth/minidlna.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjmaggard <jmaggard>2008-10-23 21:30:45 +0400
committerjmaggard <jmaggard>2008-10-23 21:30:45 +0400
commit5ce6a3290bb81e848b648dfaf815745de3bb4d45 (patch)
tree7337b08cfd3ea772643d3ff9596423396a0f8e9b
parent9c28554fd96076cded42e0921bde618fb6375a3a (diff)
Initial checkinv1
-rw-r--r--Changelog.txt433
-rw-r--r--INSTALL9
-rw-r--r--LICENCE2
-rw-r--r--Makefile126
-rw-r--r--README6
-rw-r--r--commonrdr.h35
-rw-r--r--config.h55
-rw-r--r--daemonize.c43
-rw-r--r--daemonize.h30
-rwxr-xr-xgenconfig.sh198
-rw-r--r--getifaddr.c202
-rw-r--r--getifaddr.h48
-rw-r--r--linux/miniupnpd.init.d.script40
-rw-r--r--metadata.c1590
-rw-r--r--metadata.h90
-rw-r--r--minidlna.c1024
-rw-r--r--minissdp.c499
-rw-r--r--minissdp.h31
-rw-r--r--miniupnpd.173
-rw-r--r--miniupnpd.conf73
-rw-r--r--miniupnpdpath.h47
-rw-r--r--miniupnpdtypes.h23
-rw-r--r--minixml.h30
-rw-r--r--options.c62
-rw-r--r--options.h73
-rw-r--r--scanner.c964
-rw-r--r--scanner.h83
-rw-r--r--sql.c193
-rw-r--r--sql.h32
-rw-r--r--testupnpdescgen.c55
-rw-r--r--upnpdescgen.c336
-rw-r--r--upnpdescgen.c.dlna944
-rw-r--r--upnpdescgen.h51
-rw-r--r--upnpdescstrings.h71
-rw-r--r--upnpevents.c154
-rw-r--r--upnpevents.h61
-rw-r--r--upnpglobalvars.c123
-rw-r--r--upnpglobalvars.h227
-rw-r--r--upnphttp.c1395
-rw-r--r--upnphttp.h99
-rw-r--r--upnpreplyparse.c76
-rw-r--r--upnpreplyparse.h30
-rw-r--r--upnpsoap.c1462
-rw-r--r--upnpsoap.h47
44 files changed, 4238 insertions, 7007 deletions
diff --git a/Changelog.txt b/Changelog.txt
new file mode 100644
index 0000000..42af405
--- /dev/null
+++ b/Changelog.txt
@@ -0,0 +1,433 @@
+$Id$
+
+2008/07/10:
+ Fixed compilation without ENABLE_L3F_SERVICE
+
+2008/04/27:
+ correct UNSUBSCRIBE processing
+
+2008/04/25(bis):
+ changed iptables_removeall.sh and iptables_init.sh in order
+ to remove IP from the rules
+
+VERSION 1.1 :
+2008/04/25:
+ Eventing is allmost completly implemented
+
+2008/04/24:
+ Correct event handling ?
+
+2008/04/08:
+ enabling tag in PF rules. quick can be set off.
+
+2008/03/13:
+ implementing event notify
+
+2008/03/11:
+ fixing a command line parsing error
+
+2008/03/09:
+ optimisations in upnpsoap.c
+
+2008/03/08:
+ optimizing upnpsoap.c for size
+
+2008/03/06:
+ Worked on the Eventing : generating XML event notifications
+ Send initial notification after subscribe
+ Improved pretty print of testupnpdescgen
+ Reduced Memory usage of upnpdescgen
+ fixed a small bug in the description
+
+2008/03/03:
+ Fixed miniupnpd.c for compiling without natpmp support
+ fixed presentationURL not there with L3F
+ fixing lease file creation/modification
+
+2008/02/25:
+ Rewrite of Send501() and Send404()
+ More work on events
+ genconfig.sh autodetects pf/ipf
+
+2008/02/24:
+ Started to implement UPnP Events. do NOT use it at the moment !
+
+2008/02/21:
+ Added support for the Layer3Forwarding Service
+ added init_redirect() and shutdown_redirect() functions
+
+2008/02/20:
+ Removed Ext: HTTP header when useless
+ enabled the dummy service by default to please windows XP !
+
+2008/02/07:
+ upnp_enable patch by Nikos Mavrogiannopoulos.
+ lease_file patch by Nikos Mavrogiannopoulos.
+
+2008/01/29:
+ some changes to Makefile.openwrt
+ use daemon() - daemonize() is still available for systems lacking daemon()
+
+VERSION 1.0 :
+2008/01/27:
+ moved lan_addr to upnpglobalvars.h/.c
+ Adding experimental multiple external IP support.
+
+2008/01/22:
+ removed dummy service from description to improve compatibility
+ with emule client
+ Add "secure mode". put runtime flags in the same variable
+
+2008/01/14:
+ Fixed a bug in options.c for the parsing of empty lines.
+
+2008/01/03:
+ Fixed CleanExpiredNATPMP()
+
+2008/01/02:
+ Adding a queue parameter for setting ALTQ in pf
+
+2007/12/27:
+ improving some stuff with the PF_ENABLE_FILTER_RULE.
+
+2007/12/22:
+ Adding a runtime option to enable/disable NAT-PMP
+
+2007/12/20:
+ Added a cache in linux getifstats(). Please enable by editing config.h
+
+2007/12/14:
+ Updating an existing NAT-PMP mapping now works
+
+2007/12/13:
+ NAT-PMP code now remove expired mappings
+ TCP/UDP where swapped in NAT-PMP code
+
+2007/12/04:
+ Adding details to the error message for sendto(udp_notify)
+
+2007/11/27:
+ pf code doesn't generate filter rules by default anymore. The
+ #ifdef PF_ENABLE_FILTER_RULES must be uncommented in config.h.
+
+2007/11/02:
+ moved some of the prototypes common to all firewalls to commonrdr.h
+ Added functionalities to NAT-PMP
+
+2007/11/01:
+ Debugged NAT-PMP code
+
+2007/10/28:
+ Cleaning and improving NAT-PMP code
+
+2007/10/25:
+ improved the NAT-PMP experimental support
+ updated README and INSTALL files
+
+2007/10/24:
+ Adding support for NAT-PMP (from apple !)
+
+2007/10/11:
+ Checking the commandline for errors.
+
+2007/10/08:
+ Improved the BSD/Solaris Makefile
+ Merging last code from Darren Reed. Solaris/IPF should work now !
+ added a man page.
+
+2007/10/07:
+ Adding Darren Reed code for ipf.
+
+2007/10/06:
+ Adding SunOS support thanks to Darren Reed.
+ Reorganizing os/firewall dependent code thanks to Darren Reed.
+
+2007/09/27:
+ linux make install support PREFIX variable
+
+2007/09/25:
+ reorganizing LAN sockets/address to improve multi LAN support.
+ SSDP announces are sent to all configured networks.
+ SSDP responses are "customized" by subnetwork.
+
+2007/09/24:
+ prototype code to remove unused rules
+ miniupnpdctl now display current rules
+ synchronised add_filter_rule2() prototype between pf and netfilter code.
+
+2007/09/19:
+ Correctly filling the Cache-control header in SSDP packets
+
+2007/08/28:
+ update PFRULE_INOUT_COUNTS detection for FreeBSD
+
+2007/08/27:
+ update version in genconfig.sh
+ do not error when a duplicate redirection is requested.
+
+2007/07/16:
+ really fixed the compilation bug with linux>=2.6.22
+
+2007/07/04:
+ fixed an error in options.c that prevented to use packet_log option
+
+2007/07/03:
+ improved genconfig.sh
+ fixed a compilation bug with linux>=2.6.22
+
+2007/06/22:
+ added PFRULE_INOUT_COUNTS macro to enable separate in/out packet and
+ bytes counts in pf for OpenBSD >= 3.8
+
+2007/06/15:
+ removed a possible racecondition in writepidfile()
+
+2007/06/12:
+ improved genconfig.sh : no more "echo -e", use lsb_release when available
+
+2007/06/11:
+ get_redirect_rule*() functions now return some statistics about
+ rule usage (bytes and packets)
+
+2007/06/07:
+ Fixed the get_redirect_desc() in the linux/netfilter code
+
+2007/06/05:
+ Clean up init code in miniupnpd.c
+ Added a syslog message in SoapError()
+
+2007/06/04:
+ Now store redirection descriptions in the linux/netfilter code
+
+2007/05/21:
+ Answers to SSDP M-SEARCH requests with ST: ssdp:all
+ added make install to Makefile.linux
+
+2007/05/10:
+ Fixed a bug int the DeletePortMapping linux/netfilter implementation
+ It was allways the 1st rule that was deleted.
+
+2007/04/26:
+ Fixed config.h.openwrt
+
+2007/04/16:
+ added something in the INSTALL file about the FreeBSD send(udp_notify)
+ problem fix (allowing 239.0.0.0/8 explicitely in pf.conf)
+
+2007/03/30:
+ added setsockopt(s, SOL_SOCKET, SO_BROADCAST ...) for broadcasting
+ socket
+
+2007/03/17:
+ Fixed filter rule under linux : it was using wrong port !
+ thanks to Wesley W. Terpstra
+
+2007/03/01:
+ Moved some of the SSDP code from miniupnpd.c to minissdp.c
+
+2007/02/28:
+ creating miniupnpdctl
+
+2007/02/26:
+ use LOG_MINIUPNPD macro for openlog()
+ simplify miniupndShutdown()
+
+2007/02/09:
+ improved genconfig.h
+ Added stuff to change the pf rule "rdr" to "rdr pass"
+
+2007/02/07:
+ Corrected Bytes per seconds to bits per second.
+ Ryan cleaned up comments and typos.
+ Ryan cleaned up daemonize stuff.
+ Ryan added possibility to configure model number and serial number
+
+2007/01/30:
+ ryan improved the robustness of most UPnP Soap methods
+ I added a target in the Makefiles to properly generate an uuid using
+ command line tools.
+ Improved configuration file parsing.
+
+2007/01/29:
+ Adding uuid option in miniupnpd.conf
+
+2007/01/27:
+ Added upnppermissions stuff : adding some security to UPnP !
+ fixed XML description thanks to Ryan Wagoner
+ improved QueryStateVariable thanks to Ryan Wagoner
+
+2007/01/22:
+ use getifaddr() for each GetExtenalIPAddress() Call.
+ We can change the ip during execution without pb
+
+2007/01/17:
+ Lots of code cleanup
+
+2007/01/12:
+ Fixed a nasty bug in the linux/netfilter version of get_filter_rule()
+
+2007/01/11:
+ Improved the handling of the miniupnpd.conf file.
+ added -f option to choose which config file to read.
+
+2007/01/10:
+ Fixed potential bugs with ClearNameValueList()
+
+2007/01/08:
+ All by Ryan Wagoner :
+ - coding style and comments cleanup
+ - using now option file miniupnpd.conf
+
+2007/01/03:
+ changed "xx active incoming HTTP connections" msg
+
+2007/01/02:
+ Patch from Ryan Wagoner :
+ - no need to open sockets if we can't set the error handlers
+ - format the usage so it fits nicely on a standard size terminal
+ - fix up log_err message so they have the same format and you know what
+ they are related to
+ - use same "white space" style throughout
+ - on shutdown no need to continue if opening socket or setsockopt fails
+
+2006/12/14:
+ reduce amount of log lines (keeping the same information)
+
+2006/12/07:
+ Fixed Makefiles
+ fixed typos in logs
+ version 1.0-RC1 released
+
+2006/12/02:
+ moved strings from upnpdescgen.c to upnpdescstrings.h for
+ easier modification
+ Server: HTTP header now comes from a #define
+ added a compilation-time generated config.h
+
+2006/11/30:
+ minixml updated. should have no impact
+ Added support for presentationURL with -w switch
+ implemented getifstats() for linux. Added testgetifstats program
+ improved error handling in getifstats() BSD
+
+2006/11/26:
+ no need to have miniupnpc sources to compile miniupnpd.
+ Makefile.openwrt updated
+ Closing sockets on exit thanks to Ryan Wagoner
+
+2006/11/23:
+ now handling signal SIGINT
+ setting HTTP socket with REUSEADDR thanks to Ryan Wagoner
+ daemon now tested on a Linksys WRT54G device running OpenWRT !
+
+2006/11/21:
+ disabling rtableid in pf code.
+
+2006/11/22:
+ Also responds on M-SEARCH with the uuid
+
+2006/11/20:
+ gaining some space in upnpsoap.c
+
+2006/11/19:
+ Cleaning up code to comply with ANSI C89
+
+2006/11/17:
+ Linux version now deleting both nat and accept rules
+ implemented -U option under Linux
+
+2006/11/16:
+ implemented delete_redirect_rule() for linux
+ returning error 714 in DeletePortMapping() when needed
+
+2006/11/12:
+ The linux/netfilter version should now WORK !
+ fix in the writepidfile() function. open with a mode !
+
+2006/11/10:
+ fixing the XML description generation for big endian machines
+ working on the linux/netfilter port
+
+2006/11/09:
+ improved a lot the handling of HTTP error cases
+
+2006/11/08:
+ Tried to make the Makefile compatible with both BSDmake
+ and GNUmake. It was hard because of $^ and $<
+
+2006/11/07:
+ Makefile compatible with BSD make
+ make install target.
+ getifstats.c compatible with both OpenBSD and FreeBSD.
+
+2006/11/06:
+ added getifstats.c for openBSD. May not work under FreeBSD ?
+ now reports bytes/packets sent/received
+ reporting bitrates
+ possibility to report system uptime
+
+2006/10/29:
+ added a -L option to enable loggin (is off by default now).
+
+2006/10/28:
+ Patch by Ryan Wagoner to correct the XML description (was NewUpTime
+ instead of NewUptime) and implement uptime.
+ Trying to fix the memory leak. Added some comments
+ added a -d option for debugging purpose
+ Tnaks to valgrind (under linux!) I removed a small memory access error.
+
+2006/10/27:
+ Thanks to a patch sent by Michael van Tellingen, miniupnpd is
+ now ignoring NOTIFY packets sent by other devices and is
+ writing is own pid to /var/run/miniupnpd.pid
+
+2006/10/23:
+ Allways set sendEvents="no" in XML description (was causing
+ pb with winXP as SUBSCRIBE is not implemented)
+
+2006/10/22:
+ added translation from hostname to IP in the AddPortMapping() method
+ Thanks to Ryan Wagoner.
+
+2006/10/18:
+ Added an INSTALL file
+
+2006/10/13:
+ Added the possibility to change the notify interval
+
+2006/09/29:
+ Improved compliance of the XML Descriptions
+ pretty print for testupnpdescgen
+
+2006/09/25:
+ improved the Error 404 response.
+ Better serviceType and serviceId for dummy service...
+
+2006/09/24:
+ updating the XML description generator
+
+2006/09/18:
+ Thanks to Rick Richard, support for SSDP "alive" and "byebye" notifications
+ was added. The -u options was also added. The SSDP response are now
+ improved.
+ The -o option is now working (to force a specific external IP address).
+ The Soap Methods errors are correctly responded (401 Invalid Action)
+
+2006/09/09:
+ Added code to handle filter rules. Thanks to Seth Mos (pfsense.com)
+ storing the descriptions in the label of the rule
+
+2006/09/02:
+ improved the generation of the XML descriptions.
+ I still need to add allowed values to variables.
+
+2006/07/29:
+ filtering SSDP requests and responding with same ST: field
+
+2006/07/25:
+ Added a dummy description for the WANDevice
+
+2006/07/20:
+ Command line arguments processing
+ Added possibility to listen internally on several interfaces
+
diff --git a/INSTALL b/INSTALL
index b3bbd16..fda0f97 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,18 +1,13 @@
MiniDLNA project.
-(c) 2009 Justin Maggard
+(c) 2008 Justin Maggard
Parts (c) 2006-2008 Thomas Bernard
Homepage : http://sourceforge.net/projects/minidlna/
Prerequisites :
+- taglib
- libexif
-- libjpeg
-- libid3tag
-- libFLAC
-- libvorbis
- sqlite3
-- libavformat (the ffmpeg libraries)
-- libuuid
To Build and install :
diff --git a/LICENCE b/LICENCE
index fb2a7fa..c4b67fa 100644
--- a/LICENCE
+++ b/LICENCE
@@ -1,6 +1,6 @@
MiniDLNA is distributed under version 2 of the General Public License (included
in its entirety, below). Version 2 is the only version of this license which
-this version of MiniDLNA (or modified versions derived from this one) may be
+this version of BusyBox (or modified versions derived from this one) may be
distributed under.
------------------------------------------------------------------------
diff --git a/Makefile b/Makefile
index 7fa6208..7d1c62e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,50 +1,42 @@
-# $Id: Makefile,v 1.31 2011/04/21 08:50:44 jmaggard Exp $
-# MiniDLNA project
-# http://sourceforge.net/projects/minidlna/
-# (c) 2008-2009 Justin Maggard
+# $Id$
+# MiniUPnP project
+# http://miniupnp.free.fr/
+# Author : Thomas Bernard
# for use with GNU Make
# To install use :
-# $ DESTDIR=/dummyinstalldir make install
+# $ PREFIX=/dummyinstalldir make -f Makefile.linux install
# or :
-# $ INSTALLPREFIX=/usr/local make install
+# $ INSTALLPREFIX=/usr/local make -f Makefile.linux install
# or :
-# $ make install
+# $ make -f Makefile.linux install
#
#CFLAGS = -Wall -O -D_GNU_SOURCE -g -DDEBUG
#CFLAGS = -Wall -g -Os -D_GNU_SOURCE
-CFLAGS = -Wall -g -O3 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 \
- -I/usr/include/ffmpeg \
- -I/usr/include/libavutil -I/usr/include/libavcodec -I/usr/include/libavformat \
- -I/usr/include/ffmpeg/libavutil -I/usr/include/ffmpeg/libavcodec -I/usr/include/ffmpeg/libavformat
-#STATIC_LINKING: CFLAGS += -DSTATIC
-#STATIC_LINKING: LDFLAGS = -static
+CFLAGS = -Wall -g -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
CC = gcc
RM = rm -f
INSTALL = install
-INSTALLPREFIX ?= $(DESTDIR)/usr
+INSTALLPREFIX ?= $(PREFIX)/usr
SBININSTALLDIR = $(INSTALLPREFIX)/sbin
-ETCINSTALLDIR = $(DESTDIR)/etc
+ETCINSTALLDIR = $(PREFIX)/etc/miniupnpd
BASEOBJS = minidlna.o upnphttp.o upnpdescgen.o upnpsoap.o \
upnpreplyparse.o minixml.o \
getifaddr.o daemonize.o upnpglobalvars.o \
- options.o minissdp.o uuid.o upnpevents.o \
- sql.o utils.o metadata.o scanner.o inotify.o \
- tivo_utils.o tivo_beacon.o tivo_commands.o \
- tagutils/textutils.o tagutils/misc.o tagutils/tagutils.o \
- playlist.o image_utils.o albumart.o log.o
+ options.o minissdp.o upnpevents.o \
+ sql.o metadata.o scanner.o
ALLOBJS = $(BASEOBJS) $(LNXOBJS)
-LIBS = -lpthread -lexif -ljpeg -lsqlite3 -lavformat -lavutil -lavcodec -lid3tag -lFLAC -logg -lvorbis
-#STATIC_LINKING: LIBS = -lvorbis -logg -lm -lsqlite3 -lpthread -lexif -ljpeg -lFLAC -lm -lid3tag -lz -lavformat -lavutil -lavcodec -lm
+#LIBS = -liptc
+LIBS = -lexif -ltag_c -lsqlite3 #-lgd
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
EXECUTABLES = minidlna testupnpdescgen
-.PHONY: all clean distclean install depend
+.PHONY: all clean install depend genuuid
all: $(EXECUTABLES)
@@ -53,23 +45,24 @@ clean:
$(RM) $(EXECUTABLES)
$(RM) testupnpdescgen.o
-distclean: clean
- $(RM) config.h
-
-install: minidlna
+install: minidlna genuuid
$(INSTALL) -d $(SBININSTALLDIR)
$(INSTALL) minidlna $(SBININSTALLDIR)
$(INSTALL) -d $(ETCINSTALLDIR)
+ $(INSTALL) netfilter/iptables_init.sh $(ETCINSTALLDIR)
+ $(INSTALL) netfilter/iptables_removeall.sh $(ETCINSTALLDIR)
$(INSTALL) --mode=0644 minidlna.conf $(ETCINSTALLDIR)
+ $(INSTALL) -d $(PREFIX)/etc/init.d
+ $(INSTALL) linux/miniupnpd.init.d.script $(PREFIX)/etc/init.d/miniupnpd
-minidlna: $(BASEOBJS) $(LNXOBJS) $(LIBS)
- @echo Linking $@
- @$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(BASEOBJS) $(LNXOBJS) $(LIBS)
+# genuuid is using the uuidgen CLI tool which is part of libuuid
+# from the e2fsprogs
+genuuid:
+ sed -i -e "s/^uuid=[-0-9a-f]*/uuid=`(genuuid||uuidgen) 2>/dev/null`/" minidlna.conf
+minidlna: $(BASEOBJS) $(LNXOBJS) $(LIBS)
testupnpdescgen: $(TESTUPNPDESCGENOBJS)
- @echo Linking $@
- @$(CC) $(CFLAGS) -o $@ $(TESTUPNPDESCGENOBJS)
config.h: genconfig.sh
./genconfig.sh
@@ -80,54 +73,29 @@ depend: config.h
# DO NOT DELETE
-minidlna.o: config.h upnpglobalvars.h minidlnatypes.h
-minidlna.o: upnphttp.h upnpdescgen.h minidlnapath.h getifaddr.h upnpsoap.h
-minidlna.o: options.h minissdp.h daemonize.h upnpevents.h log.h
-upnphttp.o: config.h upnphttp.h upnpdescgen.h minidlnapath.h upnpsoap.h
-upnphttp.o: upnpevents.h image_utils.h sql.h log.h icons.c
-upnpdescgen.o: config.h upnpdescgen.h minidlnapath.h upnpglobalvars.h
-upnpdescgen.o: minidlnatypes.h upnpdescstrings.h log.h
-upnpsoap.o: config.h upnpglobalvars.h minidlnatypes.h log.h utils.h sql.h
-upnpsoap.o: upnphttp.h upnpsoap.h upnpreplyparse.h getifaddr.h log.h
-upnpreplyparse.o: upnpreplyparse.h minixml.h log.h
+minidlna.o: config.h upnpglobalvars.h miniupnpdtypes.h
+minidlna.o: upnphttp.h upnpdescgen.h miniupnpdpath.h getifaddr.h upnpsoap.h
+minidlna.o: options.h minissdp.h daemonize.h upnpevents.h
+minidlna.o: commonrdr.h
+upnphttp.o: config.h upnphttp.h upnpdescgen.h miniupnpdpath.h upnpsoap.h
+upnphttp.o: upnpevents.h
+upnpdescgen.o: config.h upnpdescgen.h miniupnpdpath.h upnpglobalvars.h
+upnpdescgen.o: miniupnpdtypes.h upnpdescstrings.h
+upnpsoap.o: config.h upnpglobalvars.h miniupnpdtypes.h
+upnpsoap.o: upnphttp.h upnpsoap.h upnpreplyparse.h getifaddr.h
+upnpreplyparse.o: upnpreplyparse.h minixml.h
minixml.o: minixml.h
-getifaddr.o: getifaddr.h log.h
-daemonize.o: daemonize.h config.h log.h
+getifaddr.o: getifaddr.h
+daemonize.o: daemonize.h config.h
upnpglobalvars.o: config.h upnpglobalvars.h
-upnpglobalvars.o: minidlnatypes.h
+upnpglobalvars.o: miniupnpdtypes.h
options.o: options.h config.h upnpglobalvars.h
-options.o: minidlnatypes.h
-minissdp.o: config.h upnpdescstrings.h minidlnapath.h upnphttp.h
-minissdp.o: upnpglobalvars.h minidlnatypes.h minissdp.h log.h
-upnpevents.o: config.h upnpevents.h minidlnapath.h upnpglobalvars.h
-upnpevents.o: minidlnatypes.h upnpdescgen.h log.h uuid.h
-uuid.o: uuid.h
+options.o: miniupnpdtypes.h
+minissdp.o: config.h upnpdescstrings.h miniupnpdpath.h upnphttp.h
+minissdp.o: upnpglobalvars.h miniupnpdtypes.h minissdp.h
+upnpevents.o: config.h upnpevents.h miniupnpdpath.h upnpglobalvars.h
+upnpevents.o: miniupnpdtypes.h upnpdescgen.h
+netfilter/iptcrdr.o: netfilter/iptcrdr.h commonrdr.h config.h
testupnpdescgen.o: config.h upnpdescgen.h
-upnpdescgen.o: config.h upnpdescgen.h minidlnapath.h upnpglobalvars.h
-upnpdescgen.o: minidlnatypes.h upnpdescstrings.h
-scanner.o: upnpglobalvars.h metadata.h utils.h sql.h scanner.h log.h playlist.h
-metadata.o: upnpglobalvars.h metadata.h albumart.h utils.h sql.h log.h
-albumart.o: upnpglobalvars.h albumart.h utils.h image_utils.h sql.h log.h
-tagutils/misc.o: tagutils/misc.h
-tagutils/textutils.o: tagutils/misc.h tagutils/textutils.h log.h
-tagutils/tagutils.o: tagutils/tagutils-asf.c tagutils/tagutils-flc.c tagutils/tagutils-plist.c tagutils/tagutils-misc.c
-tagutils/tagutils.o: tagutils/tagutils-aac.c tagutils/tagutils-asf.h tagutils/tagutils-flc.h tagutils/tagutils-mp3.c tagutils/tagutils-wav.c
-tagutils/tagutils.o: tagutils/tagutils-ogg.c tagutils/tagutils-aac.h tagutils/tagutils.h tagutils/tagutils-mp3.h tagutils/tagutils-ogg.h log.h
-playlist.o: playlist.h
-inotify.o: inotify.h playlist.h
-image_utils.o: image_utils.h
-tivo_utils.o: config.h tivo_utils.h
-tivo_beacon.o: config.h tivo_beacon.h tivo_utils.h
-tivo_commands.o: config.h tivo_commands.h tivo_utils.h utils.h
-utils.o: utils.h
-sql.o: sql.h
-log.o: log.h
-
-.SUFFIXES: .c .o
-
-.c.o:
- @echo Compiling $*.c
- @$(CC) $(CFLAGS) -o $@ -c $< && exit 0;\
- echo "The following command failed:" 1>&2;\
- echo "$(CC) $(CFLAGS) -o $@ -c $<";\
- $(CC) $(CFLAGS) -o $@ -c $< &>/dev/null
+upnpdescgen.o: config.h upnpdescgen.h miniupnpdpath.h upnpglobalvars.h
+upnpdescgen.o: miniupnpdtypes.h upnpdescstrings.h
diff --git a/README b/README
index 8caead1..8111274 100644
--- a/README
+++ b/README
@@ -1,9 +1,9 @@
MiniDLNA project
-(c) 2009 Justin Maggard
-Portions (c) 2006-2007 Thomas Bernard
+(c) 2008 Justin Maggard
+Parts (c) 2006-2007 Thomas Bernard
webpage: http://sourceforge.net/projects/minidlna/
-This directory contains the MiniDLNA daemon software.
+This directory contain the MiniDLNA daemon software.
This software is subject to the conditions detailed in
the LICENCE file provided with this distribution.
diff --git a/commonrdr.h b/commonrdr.h
new file mode 100644
index 0000000..7efc3de
--- /dev/null
+++ b/commonrdr.h
@@ -0,0 +1,35 @@
+/* MiniUPnP project
+ * (c) 2006-2007 Thomas Bernard
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#ifndef __COMMONRDR_H__
+#define __COMMONRDR_H__
+
+#include "config.h"
+
+/* init and shutdown functions */
+int
+init_redirect(void);
+
+void
+shutdown_redirect(void);
+
+/* get_redirect_rule() gets internal IP and port from
+ * interface, external port and protocl
+ */
+int
+get_redirect_rule(const char * ifname, unsigned short eport, int proto,
+ char * iaddr, int iaddrlen, unsigned short * iport,
+ char * desc, int desclen,
+ u_int64_t * packets, u_int64_t * bytes);
+
+int
+get_redirect_rule_by_index(int index,
+ char * ifname, unsigned short * eport,
+ char * iaddr, int iaddrlen, unsigned short * iport,
+ int * proto, char * desc, int desclen,
+ u_int64_t * packets, u_int64_t * bytes);
+
+#endif
+
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..530d926
--- /dev/null
+++ b/config.h
@@ -0,0 +1,55 @@
+/* MiniUPnP Project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2008 Thomas Bernard
+ * generated by ./genconfig.sh on Thu Sep 11 15:05:26 PDT 2008 */
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#define UPNP_VERSION "20070827"
+#define USE_NETFILTER 1
+#define OS_NAME "JM"
+#define OS_VERSION "Linux/2.6.25.14-108.fc9.i686"
+#define OS_URL "http://www.kernel.org/"
+
+/* syslog facility to be used by miniupnpd */
+#define LOG_MINIUPNPD LOG_DAEMON
+
+/* Uncomment the following line to allow miniupnpd to be
+ * controlled by miniupnpdctl */
+/*#define USE_MINIUPNPDCTL*/
+
+/* Comment the following line to disable NAT-PMP operations */
+//#define ENABLE_NATPMP
+
+/* Uncomment the following line to enable generation of
+ * filter rules with pf */
+/*#define PF_ENABLE_FILTER_RULES*/
+
+/* Uncomment the following line to enable caching of results of
+ * the getifstats() function */
+/*#define ENABLE_GETIFSTATS_CACHING*/
+/* The cache duration is indicated in seconds */
+#define GETIFSTATS_CACHING_DURATION 2
+
+/* Uncomment the following line to enable multiple external ip support */
+/* note : Thas is EXPERIMENTAL, do not use that unless you know perfectly what you are doing */
+/*#define MULTIPLE_EXTERNAL_IP*/
+
+/* Comment the following line to use home made daemonize() func instead
+ * of BSD daemon() */
+#define USE_DAEMON
+
+/* Uncomment the following line to enable lease file support */
+/*#define ENABLE_LEASEFILE*/
+
+/* Define one or none of the two following macros in order to make some
+ * clients happy. It will change the XML Root Description of the IGD.
+ * Enabling the Layer3Forwarding Service seems to be the more compatible
+ * option. */
+/*#define HAS_DUMMY_SERVICE*/
+#define ENABLE_L3F_SERVICE
+
+/* Experimental UPnP Events support. */
+#define ENABLE_EVENTS
+
+#endif
diff --git a/daemonize.c b/daemonize.c
index b1bfe9c..4bfbc00 100644
--- a/daemonize.c
+++ b/daemonize.c
@@ -1,45 +1,22 @@
/* $Id$ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
-#include <errno.h>
+#include <syslog.h>
#include <string.h>
#include <signal.h>
#include "daemonize.h"
#include "config.h"
-#include "log.h"
#ifndef USE_DAEMON
@@ -93,24 +70,24 @@ writepidfile(const char * fname, int pid)
if(!fname || (strlen(fname) == 0))
return -1;
- if( (pidfile = open(fname, O_WRONLY|O_CREAT, 0644)) < 0)
+ if( (pidfile = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0)
{
- DPRINTF(E_ERROR, L_GENERAL, "Unable to open pidfile for writing %s: %s\n", fname, strerror(errno));
+ syslog(LOG_ERR, "Unable to open pidfile for writing %s: %m", fname);
return -1;
}
pidstringlen = snprintf(pidstring, sizeof(pidstring), "%d\n", pid);
if(pidstringlen <= 0)
{
- DPRINTF(E_ERROR, L_GENERAL,
- "Unable to write to pidfile %s: snprintf(): FAILED\n", fname);
+ syslog(LOG_ERR,
+ "Unable to write to pidfile %s: snprintf(): FAILED", fname);
close(pidfile);
return -1;
}
else
{
if(write(pidfile, pidstring, pidstringlen) < 0)
- DPRINTF(E_ERROR, L_GENERAL, "Unable to write to pidfile %s: %s\n", fname, strerror(errno));
+ syslog(LOG_ERR, "Unable to write to pidfile %s: %m", fname);
}
close(pidfile);
diff --git a/daemonize.h b/daemonize.h
index ee763bd..4939afe 100644
--- a/daemonize.h
+++ b/daemonize.h
@@ -1,31 +1,9 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#ifndef __DAEMONIZE_H__
#define __DAEMONIZE_H__
diff --git a/genconfig.sh b/genconfig.sh
index ea933fd..3d59456 100755
--- a/genconfig.sh
+++ b/genconfig.sh
@@ -1,76 +1,42 @@
#! /bin/sh
# $Id$
-# MiniDLNA project
-# http://sourceforge.net/projects/minidlna/
-#
-# MiniDLNA media server
-# Copyright (C) 2008-2009 Justin Maggard
-#
-# This file is part of MiniDLNA.
-#
-# MiniDLNA is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 as
-# published by the Free Software Foundation.
-#
-# MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
+# miniupnp daemon
+# http://miniupnp.free.fr or http://miniupnp.tuxfamily.org/
+# (c) 2006-2007 Thomas Bernard
+# This software is subject to the conditions detailed in the
+# LICENCE file provided within the distribution
RM="rm -f"
CONFIGFILE="config.h"
CONFIGMACRO="__CONFIG_H__"
-# Database path
-DB_PATH="/tmp/minidlna"
-# Log path
-LOG_PATH="${DB_PATH}"
+# version reported in XML descriptions
+UPNP_VERSION=20070827
+# Facility to syslog
+LOG_MINIUPNPD="LOG_DAEMON"
# detecting the OS name and version
OS_NAME=`uname -s`
OS_VERSION=`uname -r`
-TIVO="/*#define TIVO_SUPPORT*/"
-NETGEAR="/*#define NETGEAR*/"
-READYNAS="/*#define READYNAS*/"
-PNPX="#define PNPX 0"
-${RM} ${CONFIGFILE}
-
-# Detect if there are missing headers
-# NOTE: This check only works with a normal distro
-[ ! -e "/usr/include/sqlite3.h" ] && MISSING="libsqlite3 $MISSING"
-[ ! -e "/usr/include/jpeglib.h" ] && MISSING="libjpeg $MISSING"
-[ ! -e "/usr/include/libexif/exif-loader.h" ] && MISSING="libexif $MISSING"
-[ ! -e "/usr/include/id3tag.h" ] && MISSING="libid3tag $MISSING"
-[ ! -e "/usr/include/ogg/ogg.h" ] && MISSING="libogg $MISSING"
-[ ! -e "/usr/include/vorbis/codec.h" ] && MISSING="libvorbis $MISSING"
-[ ! -e "/usr/include/FLAC/metadata.h" ] && MISSING="libflac $MISSING"
-[ ! -e "/usr/include/ffmpeg/avutil.h" -a \
- ! -e "/usr/include/libavutil/avutil.h" -a \
- ! -e "/usr/include/ffmpeg/libavutil/avutil.h" ] && MISSING="libavutil $MISSING"
-[ ! -e "/usr/include/ffmpeg/avformat.h" -a \
- ! -e "/usr/include/libavformat/avformat.h" -a \
- ! -e "/usr/include/ffmpeg/libavformat/avformat.h" ] && MISSING="libavformat $MISSING"
-[ ! -e "/usr/include/ffmpeg/avcodec.h" -a \
- ! -e "/usr/include/libavcodec/avcodec.h" -a \
- ! -e "/usr/include/ffmpeg/libavcodec/avcodec.h" ] && MISSING="libavcodec $MISSING"
-if [ -n "$MISSING" ]; then
- echo -e "\nERROR! Cannot continue."
- echo -e "The following required libraries are either missing, or are missing development headers:\n"
- echo -e "$MISSING\n"
- exit 1
+# pfSense special case
+if [ -f /etc/platform ]; then
+ if [ `cat /etc/platform` = "pfSense" ]; then
+ OS_NAME=pfSense
+ OS_VERSION=`cat /etc/version`
+ fi
fi
-echo "/* MiniDLNA Project" >> ${CONFIGFILE}
-echo " * http://sourceforge.net/projects/minidlna/" >> ${CONFIGFILE}
-echo " * (c) 2008-2009 Justin Maggard" >> ${CONFIGFILE}
+${RM} ${CONFIGFILE}
+
+echo "/* MiniUPnP Project" >> ${CONFIGFILE}
+echo " * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/" >> ${CONFIGFILE}
+echo " * (c) 2006-2008 Thomas Bernard" >> ${CONFIGFILE}
echo " * generated by $0 on `date` */" >> ${CONFIGFILE}
echo "#ifndef $CONFIGMACRO" >> ${CONFIGFILE}
echo "#define $CONFIGMACRO" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
+echo "#define UPNP_VERSION \"$UPNP_VERSION\"" >> ${CONFIGFILE}
# OS Specific stuff
case $OS_NAME in
@@ -86,6 +52,7 @@ case $OS_NAME in
if [ \( $MAJORVER -ge 4 \) -o \( $MAJORVER -eq 3 -a $MINORVER -ge 8 \) ]; then
echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE}
fi
+ echo "#define USE_PF 1" >> ${CONFIGFILE}
OS_URL=http://www.openbsd.org/
;;
FreeBSD)
@@ -93,14 +60,25 @@ case $OS_NAME in
if [ $VER -ge 700049 ]; then
echo "#define PFRULE_INOUT_COUNTS" >> ${CONFIGFILE}
fi
+ if [ -f /usr/include/net/pfvar.h ] ; then
+ echo "#define USE_PF 1" >> ${CONFIGFILE}
+ else
+ echo "#define USE_IPF 1" >> ${CONFIGFILE}
+ fi
OS_URL=http://www.freebsd.org/
;;
pfSense)
# we need to detect if PFRULE_INOUT_COUNTS macro is needed
+ echo "#define USE_PF 1" >> ${CONFIGFILE}
OS_URL=http://www.pfsense.com/
;;
NetBSD)
OS_URL=http://www.netbsd.org/
+ if [ -f /usr/include/net/pfvar.h ] ; then
+ echo "#define USE_PF 1" >> ${CONFIGFILE}
+ else
+ echo "#define USE_IPF 1" >> ${CONFIGFILE}
+ fi
;;
SunOS)
echo "#define USE_IPF 1" >> ${CONFIGFILE}
@@ -119,55 +97,60 @@ case $OS_NAME in
KERNVERC=`echo $OS_VERSION | awk -F. '{print $3}'`
KERNVERD=`echo $OS_VERSION | awk -F. '{print $4}'`
#echo "$KERNVERA.$KERNVERB.$KERNVERC.$KERNVERD"
- # NETGEAR ReadyNAS special case
- if [ -f /etc/raidiator_version ]; then
- OS_NAME=$(awk -F'!!|=' '{ print $1 }' /etc/raidiator_version)
- OS_VERSION=$(awk -F'!!|[=,.]' '{ print $3"."$4 }' /etc/raidiator_version)
- OS_URL="http://www.readynas.com/"
- LOG_PATH="/var/log"
- DB_PATH="/var/cache/minidlna"
- TIVO="#define TIVO_SUPPORT"
- NETGEAR="#define NETGEAR"
- READYNAS="#define READYNAS"
- PNPX="#define PNPX 5"
# Debian GNU/Linux special case
- elif [ -f /etc/debian_version ]; then
+ if [ -f /etc/debian_version ]; then
OS_NAME=Debian
OS_VERSION=`cat /etc/debian_version`
OS_URL=http://www.debian.org/
- LOG_PATH="/var/log"
- # use lsb_release (Linux Standard Base) when available
- LSB_RELEASE=`which lsb_release 2>/dev/null`
- if [ 0 -eq $? ]; then
- OS_NAME=`${LSB_RELEASE} -i -s`
- OS_VERSION=`${LSB_RELEASE} -r -s`
- fi
- else
- # use lsb_release (Linux Standard Base) when available
- LSB_RELEASE=`which lsb_release 2>/dev/null`
- if [ 0 -eq $? ]; then
- OS_NAME=`${LSB_RELEASE} -i -s`
- OS_VERSION=`${LSB_RELEASE} -r -s`
- fi
fi
+ # use lsb_release (Linux Standard Base) when available
+ LSB_RELEASE=`which lsb_release`
+ if [ 0 -eq $? ]; then
+ OS_NAME=`${LSB_RELEASE} -i -s`
+ OS_VERSION=`${LSB_RELEASE} -r -s`
+ fi
+ echo "#define USE_NETFILTER 1" >> ${CONFIGFILE}
;;
*)
echo "Unknown OS : $OS_NAME"
+ echo "Please contact the author at http://miniupnp.free.fr/"
exit 1
;;
esac
-echo "#define OS_NAME \"$OS_NAME\"" >> ${CONFIGFILE}
-echo "#define OS_VERSION \"$OS_NAME/$OS_VERSION\"" >> ${CONFIGFILE}
-echo "#define OS_URL \"${OS_URL}\"" >> ${CONFIGFILE}
+echo "#define OS_NAME \"$OS_NAME\"" >> ${CONFIGFILE}
+echo "#define OS_VERSION \"$OS_NAME/$OS_VERSION\"" >> ${CONFIGFILE}
+echo "#define OS_URL \"${OS_URL}\"" >> ${CONFIGFILE}
+echo "" >> ${CONFIGFILE}
+
+echo "/* syslog facility to be used by miniupnpd */" >> ${CONFIGFILE}
+echo "#define LOG_MINIUPNPD ${LOG_MINIUPNPD}" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
-echo "/* full path of the file database */" >> ${CONFIGFILE}
-echo "#define DEFAULT_DB_PATH \"${DB_PATH}\"" >> ${CONFIGFILE}
+echo "/* Uncomment the following line to allow miniupnpd to be" >> ${CONFIGFILE}
+echo " * controlled by miniupnpdctl */" >> ${CONFIGFILE}
+echo "/*#define USE_MINIUPNPDCTL*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
-echo "/* full path of the log directory */" >> ${CONFIGFILE}
-echo "#define DEFAULT_LOG_PATH \"${LOG_PATH}\"" >> ${CONFIGFILE}
+echo "/* Comment the following line to disable NAT-PMP operations */" >> ${CONFIGFILE}
+echo "#define ENABLE_NATPMP" >> ${CONFIGFILE}
+echo "" >> ${CONFIGFILE}
+
+echo "/* Uncomment the following line to enable generation of" >> ${CONFIGFILE}
+echo " * filter rules with pf */" >> ${CONFIGFILE}
+echo "/*#define PF_ENABLE_FILTER_RULES*/">> ${CONFIGFILE}
+echo "" >> ${CONFIGFILE}
+
+echo "/* Uncomment the following line to enable caching of results of" >> ${CONFIGFILE}
+echo " * the getifstats() function */" >> ${CONFIGFILE}
+echo "/*#define ENABLE_GETIFSTATS_CACHING*/" >> ${CONFIGFILE}
+echo "/* The cache duration is indicated in seconds */" >> ${CONFIGFILE}
+echo "#define GETIFSTATS_CACHING_DURATION 2" >> ${CONFIGFILE}
+echo "" >> ${CONFIGFILE}
+
+echo "/* Uncomment the following line to enable multiple external ip support */" >> ${CONFIGFILE}
+echo "/* note : Thas is EXPERIMENTAL, do not use that unless you know perfectly what you are doing */" >> ${CONFIGFILE}
+echo "/*#define MULTIPLE_EXTERNAL_IP*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "/* Comment the following line to use home made daemonize() func instead" >> ${CONFIGFILE}
@@ -175,39 +158,20 @@ echo " * of BSD daemon() */" >> ${CONFIGFILE}
echo "#define USE_DAEMON" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
-echo "/* Enable if the system inotify.h exists. Otherwise our own inotify.h will be used. */" >> ${CONFIGFILE}
-if [ -f /usr/include/sys/inotify.h ]; then
-echo "#define HAVE_INOTIFY_H" >> ${CONFIGFILE}
-else
-echo "/*#define HAVE_INOTIFY_H*/" >> ${CONFIGFILE}
-fi
+echo "/* Uncomment the following line to enable lease file support */" >> ${CONFIGFILE}
+echo "/*#define ENABLE_LEASEFILE*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
-echo "/* Enable if the system iconv.h exists. ID3 tag reading in various character sets will not work properly otherwise. */" >> ${CONFIGFILE}
-if [ -f /usr/include/iconv.h ]; then
-echo "#define HAVE_ICONV_H" >> ${CONFIGFILE}
-else
-echo -e "\nWARNING!! Iconv support not found. ID3 tag reading may not work."
-echo "/*#define HAVE_ICONV_H*/" >> ${CONFIGFILE}
-fi
-echo "" >> ${CONFIGFILE}
-
-echo "/* Enable if the system libintl.h exists for NLS support. */" >> ${CONFIGFILE}
-if [ -f /usr/include/libintl.h ]; then
-echo "#define ENABLE_NLS" >> ${CONFIGFILE}
-else
-echo "/*#define ENABLE_NLS*/" >> ${CONFIGFILE}
-fi
+echo "/* Define one or none of the two following macros in order to make some" >> ${CONFIGFILE}
+echo " * clients happy. It will change the XML Root Description of the IGD." >> ${CONFIGFILE}
+echo " * Enabling the Layer3Forwarding Service seems to be the more compatible" >> ${CONFIGFILE}
+echo " * option. */" >> ${CONFIGFILE}
+echo "/*#define HAS_DUMMY_SERVICE*/" >> ${CONFIGFILE}
+echo "#define ENABLE_L3F_SERVICE" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
-echo "/* Enable NETGEAR-specific tweaks. */" >> ${CONFIGFILE}
-echo "${NETGEAR}" >> ${CONFIGFILE}
-echo "/* Enable ReadyNAS-specific tweaks. */" >> ${CONFIGFILE}
-echo "${READYNAS}" >> ${CONFIGFILE}
-echo "/* Compile in TiVo support. */" >> ${CONFIGFILE}
-echo "${TIVO}" >> ${CONFIGFILE}
-echo "/* Enable PnPX support. */" >> ${CONFIGFILE}
-echo "${PNPX}" >> ${CONFIGFILE}
+echo "/* Experimental UPnP Events support. */" >> ${CONFIGFILE}
+echo "/*#define ENABLE_EVENTS*/" >> ${CONFIGFILE}
echo "" >> ${CONFIGFILE}
echo "#endif" >> ${CONFIGFILE}
diff --git a/getifaddr.c b/getifaddr.c
index f0cbdfc..c7d10a0 100644
--- a/getifaddr.c
+++ b/getifaddr.c
@@ -1,35 +1,12 @@
-/* $Id: getifaddr.c,v 1.14 2011/05/02 23:50:52 jmaggard Exp $ */
+/* $Id$ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-#include <stdio.h>
-#include <stdlib.h>
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#include <string.h>
+#include <syslog.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
@@ -37,14 +14,11 @@
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
-#include <netdb.h>
-#include <errno.h>
#if defined(sun)
#include <sys/sockio.h>
#endif
#include "getifaddr.h"
-#include "log.h"
int
getifaddr(const char * ifname, char * buf, int len)
@@ -54,186 +28,28 @@ getifaddr(const char * ifname, char * buf, int len)
struct ifreq ifr;
int ifrlen;
struct sockaddr_in * addr;
- uint32_t mask;
- int i;
-
ifrlen = sizeof(ifr);
s = socket(PF_INET, SOCK_DGRAM, 0);
if(s < 0)
{
- DPRINTF(E_ERROR, L_GENERAL, "socket(PF_INET, SOCK_DGRAM): %s\n", strerror(errno));
+ syslog(LOG_ERR, "socket(PF_INET, SOCK_DGRAM): %m");
return -1;
}
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if(ioctl(s, SIOCGIFADDR, &ifr, &ifrlen) < 0)
{
- DPRINTF(E_ERROR, L_GENERAL, "ioctl(s, SIOCGIFADDR, ...): %s\n", strerror(errno));
+ syslog(LOG_ERR, "ioctl(s, SIOCGIFADDR, ...): %m");
close(s);
return -1;
}
addr = (struct sockaddr_in *)&ifr.ifr_addr;
if(!inet_ntop(AF_INET, &addr->sin_addr, buf, len))
{
- DPRINTF(E_ERROR, L_GENERAL, "inet_ntop(): %s\n", strerror(errno));
+ syslog(LOG_ERR, "inet_ntop(): %m");
close(s);
return -1;
}
- if(ioctl(s, SIOCGIFNETMASK, &ifr, &ifrlen) == 0)
- {
- addr = (struct sockaddr_in *)&ifr.ifr_netmask;
- mask = ntohl(addr->sin_addr.s_addr);
- for (i = 0; i < 32; i++)
- {
- if ((mask >> i) & 1)
- break;
- }
- mask = 32 - i;
- if (mask)
- {
- i = strlen(buf);
- snprintf(buf+i, len-i, "/%u", mask);
- }
- }
- else
- DPRINTF(E_ERROR, L_GENERAL, "ioctl(s, SIOCGIFNETMASK, ...): %s\n", strerror(errno));
close(s);
return 0;
}
-int
-getsysaddr(char * buf, int len)
-{
- int i;
- int s = socket(PF_INET, SOCK_STREAM, 0);
- struct sockaddr_in addr;
- struct ifreq ifr;
- uint32_t mask;
- int ret = -1;
-
- for (i=1; i > 0; i++)
- {
- ifr.ifr_ifindex = i;
- if( ioctl(s, SIOCGIFNAME, &ifr) < 0 )
- break;
- if(ioctl(s, SIOCGIFADDR, &ifr, sizeof(struct ifreq)) < 0)
- continue;
- memcpy(&addr, &ifr.ifr_addr, sizeof(addr));
- if(strncmp(inet_ntoa(addr.sin_addr), "127.", 4) == 0)
- continue;
- if(ioctl(s, SIOCGIFNETMASK, &ifr, sizeof(struct ifreq)) < 0)
- continue;
- if(!inet_ntop(AF_INET, &addr.sin_addr, buf, len))
- {
- DPRINTF(E_ERROR, L_GENERAL, "inet_ntop(): %s\n", strerror(errno));
- close(s);
- break;
- }
- ret = 0;
-
- memcpy(&addr, &ifr.ifr_netmask, sizeof(addr));
- mask = ntohl(addr.sin_addr.s_addr);
- for (i = 0; i < 32; i++)
- {
- if ((mask >> i) & 1)
- break;
- }
- mask = 32 - i;
- if (mask)
- {
- i = strlen(buf);
- snprintf(buf+i, len-i, "/%u", mask);
- }
- break;
- }
- close(s);
-
- return(ret);
-}
-
-int
-getsyshwaddr(char * buf, int len)
-{
- struct if_nameindex *ifaces, *if_idx;
- unsigned char mac[6];
- struct ifreq ifr;
- int fd;
- int ret = -1;
-
- memset(&mac, '\0', sizeof(mac));
- /* Get the spatially unique node identifier */
- fd = socket(AF_INET, SOCK_DGRAM, 0);
- if( fd < 0 )
- return(ret);
-
- ifaces = if_nameindex();
- if(!ifaces)
- return(ret);
-
- for(if_idx = ifaces; if_idx->if_index; if_idx++)
- {
- strncpy(ifr.ifr_name, if_idx->if_name, IFNAMSIZ);
- if(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
- continue;
- if(ifr.ifr_ifru.ifru_flags & IFF_LOOPBACK)
- continue;
- if( ioctl(fd, SIOCGIFHWADDR, &ifr) < 0 )
- continue;
- if( MACADDR_IS_ZERO(ifr.ifr_hwaddr.sa_data) )
- continue;
- ret = 0;
- break;
- }
- if_freenameindex(ifaces);
- close(fd);
-
- if(ret == 0)
- {
- if(len > 12)
- {
- memmove(mac, ifr.ifr_hwaddr.sa_data, 6);
- sprintf(buf, "%02x%02x%02x%02x%02x%02x",
- mac[0]&0xFF, mac[1]&0xFF, mac[2]&0xFF,
- mac[3]&0xFF, mac[4]&0xFF, mac[5]&0xFF);
- }
- else if(len == 6)
- {
- memmove(buf, ifr.ifr_hwaddr.sa_data, 6);
- }
- }
- return ret;
-}
-
-int
-get_remote_mac(struct in_addr ip_addr, unsigned char * mac)
-{
- struct in_addr arp_ent;
- FILE * arp;
- char remote_ip[16];
- int matches, hwtype, flags;
- memset(mac, 0xFF, 6);
-
- arp = fopen("/proc/net/arp", "r");
- if( !arp )
- return 1;
- while( !feof(arp) )
- {
- matches = fscanf(arp, "%15s 0x%8X 0x%8X %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
- remote_ip, &hwtype, &flags,
- &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
- if( matches != 9 )
- continue;
- inet_pton(AF_INET, remote_ip, &arp_ent);
- if( ip_addr.s_addr == arp_ent.s_addr )
- break;
- mac[0] = 0xFF;
- }
- fclose(arp);
-
- if( mac[0] == 0xFF )
- {
- memset(mac, 0xFF, 6);
- return 1;
- }
-
- return 0;
-}
diff --git a/getifaddr.h b/getifaddr.h
index 331a051..053c2c1 100644
--- a/getifaddr.h
+++ b/getifaddr.h
@@ -1,42 +1,11 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#ifndef __GETIFADDR_H__
#define __GETIFADDR_H__
-#include <arpa/inet.h>
-
-#define MACADDR_IS_ZERO(x) \
- ((x[0] == 0x00) && \
- (x[1] == 0x00) && \
- (x[2] == 0x00) && \
- (x[3] == 0x00) && \
- (x[4] == 0x00) && \
- (x[5] == 0x00))
/* getifaddr()
* take a network interface name and write the
@@ -45,14 +14,5 @@
int
getifaddr(const char * ifname, char * buf, int len);
-int
-getsysaddr(char * buf, int len);
-
-int
-getsyshwaddr(char * buf, int len);
-
-int
-get_remote_mac(struct in_addr ip_addr, unsigned char * mac);
-
#endif
diff --git a/linux/miniupnpd.init.d.script b/linux/miniupnpd.init.d.script
new file mode 100644
index 0000000..1f25cf0
--- /dev/null
+++ b/linux/miniupnpd.init.d.script
@@ -0,0 +1,40 @@
+#!/bin/sh
+# $Id$
+# MiniUPnP project
+# author: Thomas Bernard
+# website: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+
+MINIUPNPD=/usr/sbin/miniupnpd
+ARGS='-f /etc/miniupnpd/miniupnpd.conf'
+
+IPTABLES_CREATE=/etc/miniupnpd/iptables_init.sh
+IPTABLES_REMOVE=/etc/miniupnpd/iptables_removeall.sh
+
+test -f $MINIUPNPD || exit 0
+
+. /lib/lsb/init-functions
+
+case "$1" in
+start) log_daemon_msg "Starting miniupnpd" "miniupnpd"
+ $IPTABLES_CREATE > /dev/null 2>&1
+ start-stop-daemon --start --quiet --pidfile /var/run/miniupnpd.pid --startas $MINIUPNPD -- $ARGS $LSBNAMES
+ log_end_msg $?
+ ;;
+stop) log_daemon_msg "Stopping miniupnpd" "miniupnpd"
+ start-stop-daemon --stop --quiet --pidfile /var/run/miniupnpd.pid
+ log_end_msg $?
+ $IPTABLES_REMOVE > /dev/null 2>&1
+ ;;
+restart|reload|force-reload)
+ log_daemon_msg "Restarting miniupnpd" "miniupnpd"
+ start-stop-daemon --stop --retry 5 --quiet --pidfile /var/run/miniupnpd.pid
+ $IPTABLES_REMOVE > /dev/null 2>&1
+ $IPTABLES_CREATE > /dev/null 2>&1
+ start-stop-daemon --start --quiet --pidfile /var/run/miniupnpd.pid --startas $MINIUPNPD -- $ARGS $LSBNAMES
+ log_end_msg $?
+ ;;
+*) log_action_msg "Usage: /etc/init.d/miniupnpd {start|stop|restart|reload|force-reload}"
+ exit 2
+ ;;
+esac
+exit 0
diff --git a/metadata.c b/metadata.c
index 7cda7e7..845a4f1 100644
--- a/metadata.c
+++ b/metadata.c
@@ -1,19 +1,19 @@
-/* MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
+/* MiniDLNA media server
+ * Copyright (C) 2008 Justin Maggard
*
- * This file is part of MiniDLNA.
+ * 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.
*
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * 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.
*
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <ctype.h>
@@ -21,1464 +21,386 @@
#include <stdlib.h>
#include <sys/stat.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
+#include <sqlite3.h>
+#include <taglib/tag_c.h>
#include <libexif/exif-loader.h>
-#include "image_utils.h"
-#include <jpeglib.h>
-#include <setjmp.h>
-#include <avutil.h>
-#include <avcodec.h>
-#include <avformat.h>
-#include "tagutils/tagutils.h"
#include "upnpglobalvars.h"
-#include "upnpreplyparse.h"
#include "metadata.h"
-#include "albumart.h"
-#include "utils.h"
-#include "sql.h"
-#include "log.h"
-#ifndef FF_PROFILE_H264_BASELINE
-#define FF_PROFILE_H264_BASELINE 66
-#endif
-#ifndef FF_PROFILE_H264_MAIN
-#define FF_PROFILE_H264_MAIN 77
-#endif
-#ifndef FF_PROFILE_H264_HIGH
-#define FF_PROFILE_H264_HIGH 100
-#endif
+#define FLAG_ARTIST 0x01
-#define FLAG_TITLE 0x00000001
-#define FLAG_ARTIST 0x00000002
-#define FLAG_ALBUM 0x00000004
-#define FLAG_GENRE 0x00000008
-#define FLAG_COMMENT 0x00000010
-#define FLAG_CREATOR 0x00000020
-#define FLAG_DATE 0x00000040
-#define FLAG_DLNA_PN 0x00000080
-#define FLAG_MIME 0x00000100
-#define FLAG_DURATION 0x00000200
-#define FLAG_RESOLUTION 0x00000400
-#define FLAG_BITRATE 0x00000800
-#define FLAG_FREQUENCY 0x00001000
-#define FLAG_BPS 0x00002000
-#define FLAG_CHANNELS 0x00004000
-
-/* Audio profile flags */
-enum audio_profiles {
- PROFILE_AUDIO_UNKNOWN,
- PROFILE_AUDIO_MP3,
- PROFILE_AUDIO_AC3,
- PROFILE_AUDIO_WMA_BASE,
- PROFILE_AUDIO_WMA_FULL,
- PROFILE_AUDIO_WMA_PRO,
- PROFILE_AUDIO_MP2,
- PROFILE_AUDIO_PCM,
- PROFILE_AUDIO_AAC,
- PROFILE_AUDIO_AAC_MULT5,
- PROFILE_AUDIO_AMR
-};
-
-/* This function shamelessly copied from libdlna */
-#define MPEG_TS_SYNC_CODE 0x47
-#define MPEG_TS_PACKET_LENGTH 188 /* prepends 4 bytes to TS packet */
-#define MPEG_TS_PACKET_LENGTH_DLNA 192 /* prepends 4 bytes to TS packet */
-int
-dlna_timestamp_is_present(const char * filename)
+char *
+trim(char *str)
{
- unsigned char buffer[2*MPEG_TS_PACKET_LENGTH_DLNA+1];
- int fd, i;
-
- /* read file header */
- fd = open(filename, O_RDONLY);
- read(fd, buffer, MPEG_TS_PACKET_LENGTH_DLNA*2);
- close(fd);
- for( i=0; i < MPEG_TS_PACKET_LENGTH_DLNA; i++ )
- {
- if( buffer[i] == MPEG_TS_SYNC_CODE )
- {
- if (buffer[i + MPEG_TS_PACKET_LENGTH_DLNA] == MPEG_TS_SYNC_CODE)
- {
- if (buffer[i+MPEG_TS_PACKET_LENGTH] == 0x00 &&
- buffer[i+MPEG_TS_PACKET_LENGTH+1] == 0x00 &&
- buffer[i+MPEG_TS_PACKET_LENGTH+2] == 0x00 &&
- buffer[i+MPEG_TS_PACKET_LENGTH+3] == 0x00)
- break;
- else
- return 1;
- }
- }
- }
- return 0;
+ if (!str)
+ return(NULL);
+ int i;
+ for (i=0; i <= strlen(str) && (isspace(str[i]) || str[i] == '"'); i++) {
+ str++;
+ }
+ for (i=(strlen(str)-1); i >= 0 && (isspace(str[i]) || str[i] == '"'); i--) {
+ str[i] = '\0';
+ }
+ return str;
}
-#ifdef TIVO_SUPPORT
-int
-is_tivo_file(const char * path)
+char *
+modifyString(char * string, const char * before, const char * after, short like)
{
- unsigned char buf[5];
- unsigned char hdr[5] = { 'T','i','V','o','\0' };
- int fd;
-
- /* read file header */
- fd = open(path, O_RDONLY);
- read(fd, buf, 5);
- close(fd);
+ int oldlen, newlen, chgcnt = 0;
+ char *s, *p, *t;
- return( !memcmp(buf, hdr, 5) );
-}
-#endif
-
-void
-check_for_captions(const char * path, sqlite_int64 detailID)
-{
- char *file = malloc(PATH_MAX);
- char *id = NULL;
-
- sprintf(file, "%s", path);
- strip_ext(file);
-
- /* If we weren't given a detail ID, look for one. */
- if( !detailID )
+ oldlen = strlen(before);
+ newlen = strlen(after);
+ if( newlen > oldlen )
{
- id = sql_get_text_field(db, "SELECT ID from DETAILS where PATH glob '%q.*'"
- " and MIME glob 'video/*' limit 1", file);
- if( id )
+ s = string;
+ while( (p = strstr(s, before)) )
{
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, "New file %s looks like a caption file.\n", path);
- detailID = strtoll(id, NULL, 10);
- }
- else
- {
- //DPRINTF(E_DEBUG, L_METADATA, "No file found for caption %s.\n", path);
- goto no_source_video;
+ chgcnt++;
+ s = p+oldlen;
}
+ string = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1);
}
- strcat(file, ".srt");
- if( access(file, R_OK) == 0 )
+ s = string;
+ while( s )
{
- sql_exec(db, "INSERT into CAPTIONS"
- " (ID, PATH) "
- "VALUES"
- " (%lld, %Q)", detailID, file);
- }
-no_source_video:
- if( id )
- sqlite3_free(id);
- free(file);
-}
-
-void
-parse_nfo(const char * path, metadata_t * m)
-{
- FILE *nfo;
- char buf[65536];
- struct NameValueParserData xml;
- struct stat file;
- size_t nread;
- char *val, *val2;
-
- if( stat(path, &file) != 0 ||
- file.st_size > 65536 )
- {
- DPRINTF(E_INFO, L_METADATA, "Not parsing very large .nfo file %s\n", path);
- return;
- }
- DPRINTF(E_DEBUG, L_METADATA, "Parsing .nfo file: %s\n", path);
- nfo = fopen(path, "r");
- if( !nfo )
- return;
- nread = fread(&buf, 1, sizeof(buf), nfo);
-
- ParseNameValue(buf, nread, &xml);
-
- //printf("\ttype: %s\n", GetValueFromNameValueList(&xml, "rootElement"));
- val = GetValueFromNameValueList(&xml, "title");
- if( val )
- {
- val2 = GetValueFromNameValueList(&xml, "episodetitle");
- if( val2 )
- asprintf(&m->title, "%s - %s", val, val2);
- else
- m->title = strdup(val);
+ p = strcasestr(s, before);
+ if( !p )
+ return string;
+ if( like )
+ {
+ t = p+oldlen;
+ while( isspace(*t) )
+ t++;
+ if( *t == '"' )
+ while( *++t != '"' )
+ continue;
+ memmove(t+1, t, strlen(t)+1);
+ *t = '%';
+ }
+ memmove(p + newlen, p + oldlen, strlen(p + oldlen) + 1);
+ memcpy(p, after, newlen);
+ s = p + newlen;
}
+ if( newlen < oldlen )
+ string = realloc(string, strlen(string)+1);
- val = GetValueFromNameValueList(&xml, "plot");
- if( val )
- m->comment = strdup(val);
-
- val = GetValueFromNameValueList(&xml, "capturedate");
- if( val )
- m->date = strdup(val);
-
- val = GetValueFromNameValueList(&xml, "genre");
- if( val )
- m->genre = strdup(val);
-
- ClearNameValueList(&xml);
- fclose(nfo);
+ return string;
}
void
-free_metadata(metadata_t * m, uint32_t flags)
+strip_ext(char * name)
{
- if( m->title && (flags & FLAG_TITLE) )
- free(m->title);
- if( m->artist && (flags & FLAG_ARTIST) )
- free(m->artist);
- if( m->album && (flags & FLAG_ALBUM) )
- free(m->album);
- if( m->genre && (flags & FLAG_GENRE) )
- free(m->genre);
- if( m->creator && (flags & FLAG_CREATOR) )
- free(m->creator);
- if( m->date && (flags & FLAG_DATE) )
- free(m->date);
- if( m->comment && (flags & FLAG_COMMENT) )
- free(m->comment);
- if( m->dlna_pn && (flags & FLAG_DLNA_PN) )
- free(m->dlna_pn);
- if( m->mime && (flags & FLAG_MIME) )
- free(m->mime);
- if( m->duration && (flags & FLAG_DURATION) )
- free(m->duration);
- if( m->resolution && (flags & FLAG_RESOLUTION) )
- free(m->resolution);
- if( m->bitrate && (flags & FLAG_BITRATE) )
- free(m->bitrate);
- if( m->frequency && (flags & FLAG_FREQUENCY) )
- free(m->frequency);
- if( m->bps && (flags & FLAG_BPS) )
- free(m->bps);
- if( m->channels && (flags & FLAG_CHANNELS) )
- free(m->channels);
-}
-
-sqlite_int64
-GetFolderMetadata(const char * name, const char * path, const char * artist, const char * genre, const char * album_art)
-{
- int ret;
-
- ret = sql_exec(db, "INSERT into DETAILS"
- " (TITLE, PATH, CREATOR, ARTIST, GENRE, ALBUM_ART) "
- "VALUES"
- " ('%q', %Q, %Q, %Q, %Q, %lld);",
- name, path, artist, artist, genre,
- album_art ? strtoll(album_art, NULL, 10) : 0);
- if( ret != SQLITE_OK )
- ret = 0;
- else
- ret = sqlite3_last_insert_rowid(db);
-
- return ret;
+ if( rindex(name, '.') )
+ *rindex(name, '.') = '\0';
}
sqlite_int64
GetAudioMetadata(const char * path, char * name)
{
- char type[4];
- static char lang[6] = { '\0' };
+ size_t size = 0;
+ char date[16], duration[16], dlna_pn[24], mime[16];
struct stat file;
+ int seconds, minutes;
sqlite_int64 ret;
- char *esc_tag;
- int i;
- sqlite_int64 album_art = 0;
- struct song_metadata song;
- metadata_t m;
- uint32_t free_flags = FLAG_MIME|FLAG_DURATION|FLAG_DLNA_PN|FLAG_DATE;
- memset(&m, '\0', sizeof(metadata_t));
-
- if ( stat(path, &file) != 0 )
+ TagLib_File *audio_file;
+ TagLib_Tag *tag;
+ const TagLib_AudioProperties *properties;
+ char *sql;
+ char *zErrMsg = NULL;
+ char *title, *artist, *album, *genre, *comment;
+ int free_flags = 0;
+
+ if ( stat(path, &file) == 0 )
+ size = file.st_size;
+ else
return 0;
strip_ext(name);
- if( ends_with(path, ".mp3") )
- {
- strcpy(type, "mp3");
- m.mime = strdup("audio/mpeg");
- }
- else if( ends_with(path, ".m4a") || ends_with(path, ".mp4") ||
- ends_with(path, ".aac") || ends_with(path, ".m4p") )
- {
- strcpy(type, "aac");
- m.mime = strdup("audio/mp4");
- }
- else if( ends_with(path, ".3gp") )
- {
- strcpy(type, "aac");
- m.mime = strdup("audio/3gpp");
- }
- else if( ends_with(path, ".wma") || ends_with(path, ".asf") )
- {
- strcpy(type, "asf");
- m.mime = strdup("audio/x-ms-wma");
- }
- else if( ends_with(path, ".flac") || ends_with(path, ".fla") || ends_with(path, ".flc") )
- {
- strcpy(type, "flc");
- m.mime = strdup("audio/x-flac");
- }
- else if( ends_with(path, ".wav") )
- {
- strcpy(type, "wav");
- m.mime = strdup("audio/x-wav");
- }
- else if( ends_with(path, ".ogg") || ends_with(path, ".oga") )
- {
- strcpy(type, "ogg");
- m.mime = strdup("audio/ogg");
- }
- else if( ends_with(path, ".pcm") )
+ taglib_set_strings_unicode(1);
+
+ audio_file = taglib_file_new(path);
+ if(audio_file == NULL)
+ return 0;
+
+ tag = taglib_file_tag(audio_file);
+ properties = taglib_file_audioproperties(audio_file);
+
+ seconds = taglib_audioproperties_length(properties) % 60;
+ minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
+
+ date[0] = '\0';
+ if( taglib_tag_year(tag) )
+ sprintf(date, "%04d-01-01", taglib_tag_year(tag));
+ sprintf(duration, "%d:%02d:%02d.000", minutes/60, minutes, seconds);
+
+ title = taglib_tag_title(tag);
+ if( strlen(title) )
{
- strcpy(type, "pcm");
- m.mime = strdup("audio/L16");
+ title = trim(title);
+ if( index(title, '&') )
+ {
+ title = modifyString(strdup(title), "&", "&amp;amp;", 0);
+ }
}
else
{
- DPRINTF(E_WARN, L_GENERAL, "Unhandled file extension on %s\n", path);
- return 0;
+ title = name;
}
-
- if( !(*lang) )
+ artist = taglib_tag_artist(tag);
+ if( strlen(artist) )
{
- if( !getenv("LANG") )
- strcpy(lang, "en_US");
- else
- strncpy(lang, getenv("LANG"), 5);
- lang[5] = '\0';
+ artist = trim(artist);
+ if( index(artist, '&') )
+ {
+ free_flags |= FLAG_ARTIST;
+ artist = modifyString(strdup(artist), "&", "&amp;amp;", 0);
+ }
}
-
- if( readtags((char *)path, &song, &file, lang, type) != 0 )
+ else
{
- DPRINTF(E_WARN, L_GENERAL, "Cannot extract tags from %s!\n", path);
- freetags(&song);
- free_metadata(&m, free_flags);
- return 0;
+ artist = NULL;
}
-
- if( song.dlna_pn )
- asprintf(&m.dlna_pn, "%s;DLNA.ORG_OP=01;DLNA.ORG_CI=0", song.dlna_pn);
- if( song.year )
- asprintf(&m.date, "%04d-01-01", song.year);
- asprintf(&m.duration, "%d:%02d:%02d.%03d",
- (song.song_length/3600000),
- (song.song_length/60000%60),
- (song.song_length/1000%60),
- (song.song_length%1000));
- if( song.title && *song.title )
+ album = taglib_tag_album(tag);
+ if( strlen(album) )
{
- m.title = trim(song.title);
- if( (esc_tag = escape_tag(m.title, 0)) )
+ album = trim(album);
+ if( index(album, '&') )
{
- free_flags |= FLAG_TITLE;
- m.title = esc_tag;
+ album = modifyString(strdup(album), "&", "&amp;amp;", 0);
}
}
else
{
- m.title = name;
+ album = NULL;
}
- for( i=ROLE_START; i<N_ROLE; i++ )
+ genre = taglib_tag_genre(tag);
+ if( strlen(genre) )
{
- if( song.contributor[i] && *song.contributor[i] )
+ genre = trim(genre);
+ if( index(genre, '&') )
{
- m.creator = trim(song.contributor[i]);
- if( strlen(m.creator) > 48 )
- {
- m.creator = strdup("Various Artists");
- free_flags |= FLAG_CREATOR;
- }
- else if( (esc_tag = escape_tag(m.creator, 0)) )
- {
- m.creator = esc_tag;
- free_flags |= FLAG_CREATOR;
- }
- m.artist = m.creator;
- break;
+ genre = modifyString(strdup(genre), "&", "&amp;amp;", 0);
}
}
- /* If there is a band associated with the album, use it for virtual containers. */
- if( (i != ROLE_BAND) && (i != ROLE_ALBUMARTIST) )
+ else
{
- if( song.contributor[ROLE_BAND] && *song.contributor[ROLE_BAND] )
- {
- i = ROLE_BAND;
- m.artist = trim(song.contributor[i]);
- if( strlen(m.artist) > 48 )
- {
- m.artist = strdup("Various Artists");
- free_flags |= FLAG_ARTIST;
- }
- else if( (esc_tag = escape_tag(m.artist, 0)) )
- {
- m.artist = esc_tag;
- free_flags |= FLAG_ARTIST;
- }
- }
+ genre = NULL;
}
- if( song.album && *song.album )
+ comment = taglib_tag_comment(tag);
+ if( strlen(comment) )
{
- m.album = trim(song.album);
- if( (esc_tag = escape_tag(m.album, 0)) )
+ comment = trim(comment);
+ if( index(comment, '&') )
{
- free_flags |= FLAG_ALBUM;
- m.album = esc_tag;
+ comment = modifyString(strdup(comment), "&", "&amp;amp;", 0);
}
}
- if( song.genre && *song.genre )
+ else
{
- m.genre = trim(song.genre);
- if( (esc_tag = escape_tag(m.genre, 0)) )
- {
- free_flags |= FLAG_GENRE;
- m.genre = esc_tag;
- }
+ comment = NULL;
}
- if( song.comment && *song.comment )
+
+
+ if( 1 ) // Switch on audio file type
{
- m.comment = trim(song.comment);
- if( (esc_tag = escape_tag(m.comment, 0)) )
- {
- free_flags |= FLAG_COMMENT;
- m.comment = esc_tag;
- }
+ strcpy(dlna_pn, "MP3;DLNA.ORG_OP=01");
+ strcpy(mime, "audio/mpeg");
}
- album_art = find_album_art(path, song.image, song.image_size);
+ sql = sqlite3_mprintf( "INSERT into DETAILS"
+ " (SIZE, DURATION, CHANNELS, BITRATE, SAMPLERATE, DATE,"
+ " TITLE, ARTIST, ALBUM, GENRE, COMMENT, TRACK, DLNA_PN, MIME) "
+ "VALUES"
+ " (%d, '%s', %d, %d, %d, %Q, %Q, %Q, %Q, %Q, %Q, %d, '%s', '%s');",
+ size, duration, taglib_audioproperties_channels(properties),
+ taglib_audioproperties_bitrate(properties)*1024,
+ taglib_audioproperties_samplerate(properties),
+ strlen(date) ? date : NULL,
+ title,
+ artist,
+ album,
+ genre,
+ comment,
+ taglib_tag_track(tag),
+ dlna_pn, mime);
+
+ taglib_tag_free_strings();
+ taglib_file_free(audio_file);
+
+ if( free_flags & FLAG_ARTIST )
+ free(artist);
- ret = sql_exec(db, "INSERT into DETAILS"
- " (PATH, SIZE, TIMESTAMP, DURATION, CHANNELS, BITRATE, SAMPLERATE, DATE,"
- " TITLE, CREATOR, ARTIST, ALBUM, GENRE, COMMENT, DISC, TRACK, DLNA_PN, MIME, ALBUM_ART) "
- "VALUES"
- " (%Q, %lld, %ld, '%s', %d, %d, %d, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %d, %d, %Q, '%s', %lld);",
- path, file.st_size, file.st_mtime, m.duration, song.channels, song.bitrate, song.samplerate, m.date,
- m.title, m.creator, m.artist, m.album, m.genre, m.comment, song.disc, song.track,
- m.dlna_pn, song.mime?song.mime:m.mime, album_art);
- if( ret != SQLITE_OK )
+ //DEBUG printf("SQL: %s\n", sql);
+ if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
{
- fprintf(stderr, "Error inserting details for '%s'!\n", path);
+ fprintf(stderr, "Error inserting details for '%s'! [%s]\n", path, zErrMsg);
+ if (zErrMsg)
+ sqlite3_free(zErrMsg);
ret = 0;
}
else
{
ret = sqlite3_last_insert_rowid(db);
}
- freetags(&song);
- free_metadata(&m, free_flags);
-
+ sqlite3_free(sql);
return ret;
}
-/* For libjpeg error handling */
-jmp_buf setjmp_buffer;
-static void
-libjpeg_error_handler(j_common_ptr cinfo)
-{
- cinfo->err->output_message (cinfo);
- longjmp(setjmp_buffer, 1);
- return;
-}
-
sqlite_int64
GetImageMetadata(const char * path, char * name)
{
ExifData *ed;
ExifEntry *e = NULL;
- ExifLoader *l;
- struct jpeg_decompress_struct cinfo;
- struct jpeg_error_mgr jerr;
- FILE *infile;
+ ExifTag tag;
int width=0, height=0, thumb=0;
- char make[32], model[64] = {'\0'};
+ size_t size;
+ char date[64], make[32], model[64], dlna_pn[64];
char b[1024];
struct stat file;
sqlite_int64 ret;
- image_s * imsrc;
- metadata_t m;
- uint32_t free_flags = 0xFFFFFFFF;
- memset(&m, '\0', sizeof(metadata_t));
+ char *sql;
+ char *zErrMsg = NULL;
+
+ date[0] = '\0';
+ model[0] = '\0';
+ dlna_pn[0] = '\0';
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, "Parsing %s...\n", path);
- if ( stat(path, &file) != 0 )
+ //DEBUG printf("Parsing %s...\n", path);
+ if ( stat(path, &file) == 0 )
+ size = file.st_size;
+ else
return 0;
strip_ext(name);
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * size: %jd\n", file.st_size);
+ //DEBUG printf(" * size: %d\n", size);
- /* MIME hard-coded to JPEG for now, until we add PNG support */
- asprintf(&m.mime, "image/jpeg");
-
- l = exif_loader_new();
+ ExifLoader * l = exif_loader_new();
exif_loader_write_file(l, path);
ed = exif_loader_get_data(l);
exif_loader_unref(l);
- if( !ed )
- goto no_exifdata;
- e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_ORIGINAL);
- if( e || (e = exif_content_get_entry(ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_DIGITIZED)) ) {
- m.date = strdup(exif_entry_get_value(e, b, sizeof(b)));
- if( strlen(m.date) > 10 )
+ tag = EXIF_TAG_PIXEL_X_DIMENSION;
+ e = exif_content_get_entry(ed->ifd[EXIF_IFD_EXIF], tag);
+ if( e )
+ width = atoi( exif_entry_get_value(e, b, sizeof(b)) );
+
+ tag = EXIF_TAG_PIXEL_Y_DIMENSION;
+ e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], tag);
+ if( e )
+ height = atoi( exif_entry_get_value(e, b, sizeof(b)) );
+ //DEBUG printf(" * resolution: %dx%d\n", width, height);
+
+ tag = EXIF_TAG_DATE_TIME_ORIGINAL;
+ e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], tag);
+ if( e ) {
+ strncpy(date, exif_entry_get_value(e, b, sizeof(b)), sizeof(date));
+ if( strlen(date) > 10 )
{
- m.date[4] = '-';
- m.date[7] = '-';
- m.date[10] = 'T';
+ date[4] = '-';
+ date[7] = '-';
+ date[10] = 'T';
}
else {
- free(m.date);
- m.date = NULL;
+ strcpy(date, "0000-00-00");
}
}
else {
- /* One last effort to get the date from XMP */
- image_get_jpeg_date_xmp(path, &m.date);
+ strcpy(date, "0000-00-00");
}
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * date: %s\n", m.date);
+ //DEBUG printf(" * date: %s\n", date);
- e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_MAKE);
+ model[0] = '\0';
+ tag = EXIF_TAG_MAKE;
+ e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], tag);
if( e )
{
strncpy(make, exif_entry_get_value(e, b, sizeof(b)), sizeof(make));
- e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_MODEL);
+ tag = EXIF_TAG_MODEL;
+ e = exif_content_get_entry (ed->ifd[EXIF_IFD_0], tag);
if( e )
{
strncpy(model, exif_entry_get_value(e, b, sizeof(b)), sizeof(model));
if( !strcasestr(model, make) )
snprintf(model, sizeof(model), "%s %s", make, exif_entry_get_value(e, b, sizeof(b)));
- m.creator = strdup(model);
}
}
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * model: %s\n", model);
+ if( !strlen(model) )
+ strcpy(model, "Unknown");
+ //DEBUG printf(" * model: %s\n", model);
if( ed->size )
- {
- /* We might need to verify that the thumbnail is 160x160 or smaller */
- if( ed->size > 12000 )
- {
- imsrc = image_new_from_jpeg(NULL, 0, (char *)ed->data, ed->size, 1);
- if( imsrc )
- {
- if( (imsrc->width <= 160) && (imsrc->height <= 160) )
- thumb = 1;
- image_free(imsrc);
- }
- }
- else
- thumb = 1;
- }
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * thumbnail: %d\n", thumb);
+ thumb = 1;
+ else
+ thumb = 0;
+ //DEBUG printf(" * thumbnail: %d\n", thumb);
exif_data_unref(ed);
-no_exifdata:
- /* If SOF parsing fails, then fall through to reading the JPEG data with libjpeg to get the resolution */
- if( image_get_jpeg_resolution(path, &width, &height) != 0 || !width || !height )
- {
- infile = fopen(path, "r");
- cinfo.err = jpeg_std_error(&jerr);
- jerr.error_exit = libjpeg_error_handler;
- jpeg_create_decompress(&cinfo);
- if( setjmp(setjmp_buffer) )
- goto error;
- jpeg_stdio_src(&cinfo, infile);
- jpeg_read_header(&cinfo, TRUE);
- jpeg_start_decompress(&cinfo);
- width = cinfo.output_width;
- height = cinfo.output_height;
- error:
- jpeg_destroy_decompress(&cinfo);
- fclose(infile);
- }
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * resolution: %dx%d\n", width, height);
-
- if( !width || !height )
- {
- free_metadata(&m, free_flags);
- return 0;
- }
if( width <= 640 && height <= 480 )
- asprintf(&m.dlna_pn, "JPEG_SM;%s", dlna_no_conv);
+ strcpy(dlna_pn, "JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
else if( width <= 1024 && height <= 768 )
- asprintf(&m.dlna_pn, "JPEG_MED;%s", dlna_no_conv);
- else if( (width <= 4096 && height <= 4096) || !(GETFLAG(DLNA_STRICT_MASK)) )
- asprintf(&m.dlna_pn, "JPEG_LRG;%s", dlna_no_conv);
- asprintf(&m.resolution, "%dx%d", width, height);
-
- ret = sql_exec(db, "INSERT into DETAILS"
- " (PATH, TITLE, SIZE, TIMESTAMP, DATE, RESOLUTION, THUMBNAIL, CREATOR, DLNA_PN, MIME) "
- "VALUES"
- " (%Q, '%q', %lld, %ld, %Q, %Q, %d, %Q, %Q, %Q);",
- path, name, file.st_size, file.st_mtime, m.date, m.resolution, thumb, m.creator, m.dlna_pn, m.mime);
- if( ret != SQLITE_OK )
- {
- fprintf(stderr, "Error inserting details for '%s'!\n", path);
+ strcpy(dlna_pn, "JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
+ else if( width <= 4096 && height <= 4096 )
+ strcpy(dlna_pn, "JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
+ else
+ strcpy(dlna_pn, "JPEG_XL");
+
+ sql = sqlite3_mprintf( "INSERT into DETAILS"
+ " (TITLE, SIZE, DATE, WIDTH, HEIGHT, THUMBNAIL, CREATOR, DLNA_PN, MIME) "
+ "VALUES"
+ " ('%q', %d, '%s', %d, %d, %d, '%q', '%s', '%s');",
+ name, size, date, width, height, thumb, model, dlna_pn, "image/jpeg");
+ //DEBUG printf("SQL: %s\n", sql);
+ if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
+ {
+ fprintf(stderr, "Error inserting details for '%s'! [%s]\n", path, zErrMsg);
+ if (zErrMsg)
+ sqlite3_free(zErrMsg);
ret = 0;
}
else
{
ret = sqlite3_last_insert_rowid(db);
}
- free_metadata(&m, free_flags);
-
+ sqlite3_free(sql);
return ret;
}
sqlite_int64
GetVideoMetadata(const char * path, char * name)
{
+ size_t size = 0;
struct stat file;
- int ret, i;
- struct tm *modtime;
- AVFormatContext *ctx;
- AVCodecContext *ac = NULL, *vc = NULL;
- int audio_stream = -1, video_stream = -1;
- enum audio_profiles audio_profile = PROFILE_AUDIO_UNKNOWN;
- tsinfo_t *ts;
- char fourcc[4];
- sqlite_int64 album_art = 0;
- char nfo[PATH_MAX], *ext;
- struct song_metadata video;
- metadata_t m;
- uint32_t free_flags = 0xFFFFFFFF;
- memset(&m, '\0', sizeof(m));
- memset(&video, '\0', sizeof(video));
+ char *sql;
+ char *zErrMsg = NULL;
+ int ret;
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, "Parsing video %s...\n", name);
- if ( stat(path, &file) != 0 )
- return 0;
+ //DEBUG printf("Parsing %s...\n", path);
+ if ( stat(path, &file) == 0 )
+ size = file.st_size;
strip_ext(name);
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, " * size: %jd\n", file.st_size);
-
- av_register_all();
- if( av_open_input_file(&ctx, path, NULL, 0, NULL) != 0 )
- {
- DPRINTF(E_WARN, L_METADATA, "Opening %s failed!\n", path);
- return 0;
- }
- av_find_stream_info(ctx);
- //dump_format(ctx, 0, NULL, 0);
- for( i=0; i<ctx->nb_streams; i++)
- {
- if( audio_stream == -1 &&
- ctx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO )
- {
- audio_stream = i;
- ac = ctx->streams[audio_stream]->codec;
- continue;
- }
- else if( video_stream == -1 &&
- ctx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
- {
- video_stream = i;
- vc = ctx->streams[video_stream]->codec;
- continue;
- }
- }
- /* This must not be a video file. */
- if( !vc )
- {
- av_close_input_file(ctx);
- if( !is_audio(path) )
- DPRINTF(E_DEBUG, L_METADATA, "File %s does not contain a video stream.\n", basename(path));
- return 0;
- }
-
- strcpy(nfo, path);
- ext = strrchr(nfo, '.');
- if( ext )
- {
- strcpy(ext+1, "nfo");
- if( access(nfo, F_OK) == 0 )
- {
- parse_nfo(nfo, &m);
- }
- }
-
- if( !m.date )
- {
- m.date = malloc(20);
- modtime = localtime(&file.st_mtime);
- strftime(m.date, 20, "%FT%T", modtime);
- }
-
- if( ac )
- {
- aac_object_type_t aac_type = AAC_INVALID;
- switch( ac->codec_id )
- {
- case CODEC_ID_MP3:
- audio_profile = PROFILE_AUDIO_MP3;
- break;
- case CODEC_ID_AAC:
- if( !ac->extradata_size ||
- !ac->extradata )
- {
- DPRINTF(E_DEBUG, L_METADATA, "No AAC type\n");
- }
- else
- {
- uint8_t data;
- memcpy(&data, ac->extradata, 1);
- aac_type = data >> 3;
- }
- switch( aac_type )
- {
- /* AAC Low Complexity variants */
- case AAC_LC:
- case AAC_LC_ER:
- if( ac->sample_rate < 8000 ||
- ac->sample_rate > 48000 )
- {
- DPRINTF(E_DEBUG, L_METADATA, "Unsupported AAC: sample rate is not 8000 < %d < 48000\n",
- ac->sample_rate);
- break;
- }
- /* AAC @ Level 1/2 */
- if( ac->channels <= 2 &&
- ac->bit_rate <= 576000 )
- audio_profile = PROFILE_AUDIO_AAC;
- else if( ac->channels <= 6 &&
- ac->bit_rate <= 1440000 )
- audio_profile = PROFILE_AUDIO_AAC_MULT5;
- else
- DPRINTF(E_DEBUG, L_METADATA, "Unhandled AAC: %d channels, %d bitrate\n",
- ac->channels,
- ac->bit_rate);
- break;
- default:
- DPRINTF(E_DEBUG, L_METADATA, "Unhandled AAC type [%d]\n", aac_type);
- break;
- }
- break;
- case CODEC_ID_AC3:
- case CODEC_ID_DTS:
- audio_profile = PROFILE_AUDIO_AC3;
- break;
- case CODEC_ID_WMAV1:
- case CODEC_ID_WMAV2:
- /* WMA Baseline: stereo, up to 48 KHz, up to 192,999 bps */
- if ( ac->bit_rate <= 193000 )
- audio_profile = PROFILE_AUDIO_WMA_BASE;
- /* WMA Full: stereo, up to 48 KHz, up to 385 Kbps */
- else if ( ac->bit_rate <= 385000 )
- audio_profile = PROFILE_AUDIO_WMA_FULL;
- break;
- #if LIBAVCODEC_VERSION_INT > ((51<<16)+(50<<8)+1)
- case CODEC_ID_WMAPRO:
- audio_profile = PROFILE_AUDIO_WMA_PRO;
- break;
- #endif
- case CODEC_ID_MP2:
- audio_profile = PROFILE_AUDIO_MP2;
- break;
- case CODEC_ID_AMR_NB:
- audio_profile = PROFILE_AUDIO_AMR;
- break;
- default:
- if( (ac->codec_id >= CODEC_ID_PCM_S16LE) &&
- (ac->codec_id < CODEC_ID_ADPCM_IMA_QT) )
- audio_profile = PROFILE_AUDIO_PCM;
- else
- DPRINTF(E_DEBUG, L_METADATA, "Unhandled audio codec [0x%X]\n", ac->codec_id);
- break;
- }
- asprintf(&m.frequency, "%u", ac->sample_rate);
- #if LIBAVCODEC_VERSION_INT < (52<<16)
- asprintf(&m.bps, "%u", ac->bits_per_sample);
- #else
- asprintf(&m.bps, "%u", ac->bits_per_coded_sample);
- #endif
- asprintf(&m.channels, "%u", ac->channels);
- }
- if( vc )
- {
- int off;
- int duration, hours, min, sec, ms;
- ts_timestamp_t ts_timestamp = NONE;
- DPRINTF(E_DEBUG, L_METADATA, "Container: '%s' [%s]\n", ctx->iformat->name, basename(path));
- asprintf(&m.resolution, "%dx%d", vc->width, vc->height);
- if( ctx->bit_rate > 8 )
- asprintf(&m.bitrate, "%u", ctx->bit_rate / 8);
- if( ctx->duration > 0 ) {
- duration = (int)(ctx->duration / AV_TIME_BASE);
- hours = (int)(duration / 3600);
- min = (int)(duration / 60 % 60);
- sec = (int)(duration % 60);
- ms = (int)(ctx->duration / (AV_TIME_BASE/1000) % 1000);
- asprintf(&m.duration, "%d:%02d:%02d.%03d", hours, min, sec, ms);
- }
-
- /* NOTE: The DLNA spec only provides for ASF (WMV), TS, PS, and MP4 containers. Skip DLNA parsing for everything else. */
- if( strcmp(ctx->iformat->name, "avi") == 0 )
- {
- asprintf(&m.mime, "video/x-msvideo");
- if( vc->codec_id == CODEC_ID_MPEG4 )
- {
- fourcc[0] = vc->codec_tag & 0xff;
- fourcc[1] = vc->codec_tag>>8 & 0xff;
- fourcc[2] = vc->codec_tag>>16 & 0xff;
- fourcc[3] = vc->codec_tag>>24 & 0xff;
- if( memcmp(fourcc, "XVID", 4) == 0 ||
- memcmp(fourcc, "DX50", 4) == 0 ||
- memcmp(fourcc, "DIVX", 4) == 0 )
- asprintf(&m.creator, "DiVX");
- }
- }
- else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 &&
- ends_with(path, ".mov") )
- asprintf(&m.mime, "video/quicktime");
- else if( strncmp(ctx->iformat->name, "matroska", 8) == 0 )
- asprintf(&m.mime, "video/x-matroska");
- else if( strcmp(ctx->iformat->name, "flv") == 0 )
- asprintf(&m.mime, "video/x-flv");
- if( m.mime )
- goto video_no_dlna;
-
- switch( vc->codec_id )
- {
- case CODEC_ID_MPEG1VIDEO:
- if( strcmp(ctx->iformat->name, "mpeg") == 0 )
- {
- if( (vc->width == 352) &&
- (vc->height <= 288) )
- {
- asprintf(&m.dlna_pn, "MPEG1;%s", dlna_no_conv);
- }
- asprintf(&m.mime, "video/mpeg");
- }
- break;
- case CODEC_ID_MPEG2VIDEO:
- m.dlna_pn = malloc(64);
- off = sprintf(m.dlna_pn, "MPEG_");
- if( strcmp(ctx->iformat->name, "mpegts") == 0 )
- {
- DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s MPEG2 TS\n", video_stream, basename(path), m.resolution);
- off += sprintf(m.dlna_pn+off, "TS_");
- if( (vc->width >= 1280) &&
- (vc->height >= 720) )
- {
- off += sprintf(m.dlna_pn+off, "HD_NA");
- }
- else
- {
- off += sprintf(m.dlna_pn+off, "SD_");
- if( (vc->height == 576) ||
- (vc->height == 288) )
- off += sprintf(m.dlna_pn+off, "EU");
- else
- off += sprintf(m.dlna_pn+off, "NA");
- }
- ts = ctx->priv_data;
- if( ts->packet_size == 192 )
- {
- if( dlna_timestamp_is_present(path) )
- ts_timestamp = VALID;
- else
- ts_timestamp = EMPTY;
- }
- else if( ts->packet_size != 188 )
- {
- DPRINTF(E_DEBUG, L_METADATA, "Invalid TS packet size [%s]\n", basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- }
- switch( ts_timestamp )
- {
- case NONE:
- asprintf(&m.mime, "video/mpeg");
- off += sprintf(m.dlna_pn+off, "_ISO");
- break;
- case VALID:
- off += sprintf(m.dlna_pn+off, "_T");
- case EMPTY:
- asprintf(&m.mime, "video/vnd.dlna.mpeg-tts");
- default:
- break;
- }
- }
- else if( strcmp(ctx->iformat->name, "mpeg") == 0 )
- {
- DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s MPEG2 PS\n", video_stream, basename(path), m.resolution);
- off += sprintf(m.dlna_pn+off, "PS_");
- if( (vc->height == 576) ||
- (vc->height == 288) )
- off += sprintf(m.dlna_pn+off, "PAL");
- else
- off += sprintf(m.dlna_pn+off, "NTSC");
- asprintf(&m.mime, "video/mpeg");
- }
- else
- {
- DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s [UNKNOWN CONTAINER] is %s MPEG2\n", video_stream, basename(path), m.resolution);
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- }
- if( m.dlna_pn )
- sprintf(m.dlna_pn+off, ";%s", dlna_no_conv);
- break;
- case CODEC_ID_H264:
- m.dlna_pn = malloc(128);
- off = sprintf(m.dlna_pn, "AVC_");
-
- if( strcmp(ctx->iformat->name, "mpegts") == 0 )
- {
- off += sprintf(m.dlna_pn+off, "TS_");
- switch( vc->profile )
- {
- case FF_PROFILE_H264_BASELINE:
- off += sprintf(m.dlna_pn+off, "BL_");
- if( vc->width <= 352 &&
- vc->height <= 288 &&
- vc->bit_rate <= 384000 )
- {
- off += sprintf(m.dlna_pn+off, "CIF15_");
- break;
- }
- else if( vc->width <= 352 &&
- vc->height <= 288 &&
- vc->bit_rate <= 3000000 )
- {
- off += sprintf(m.dlna_pn+off, "CIF30_");
- break;
- }
- /* Fall back to Main Profile if it doesn't match a Baseline DLNA profile. */
- else
- off -= 3;
- default:
- case FF_PROFILE_H264_MAIN:
- off += sprintf(m.dlna_pn+off, "MP_");
- if( vc->profile != FF_PROFILE_H264_BASELINE &&
- vc->profile != FF_PROFILE_H264_MAIN )
- {
- DPRINTF(E_DEBUG, L_METADATA, "Unknown AVC profile %d; assuming MP. [%s]\n",
- vc->profile, basename(path));
- }
- if( vc->width <= 720 &&
- vc->height <= 576 &&
- vc->bit_rate <= 10000000 )
- {
- off += sprintf(m.dlna_pn+off, "SD_");
- }
- else if( vc->width <= 1920 &&
- vc->height <= 1152 &&
- vc->bit_rate <= 20000000 )
- {
- off += sprintf(m.dlna_pn+off, "HD_");
- }
- else
- {
- DPRINTF(E_DEBUG, L_METADATA, "Unsupported h.264 video profile! [%s, %dx%d, %dbps : %s]\n",
- m.dlna_pn,
- vc->width,
- vc->height,
- vc->bit_rate,
- basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- }
- break;
- case FF_PROFILE_H264_HIGH:
- off += sprintf(m.dlna_pn+off, "HP_");
- if( vc->width <= 1920 &&
- vc->height <= 1152 &&
- vc->bit_rate <= 30000000 &&
- audio_profile == PROFILE_AUDIO_AC3 )
- {
- off += sprintf(m.dlna_pn+off, "HD_");
- }
- else
- {
- DPRINTF(E_DEBUG, L_METADATA, "Unsupported h.264 HP video profile! [%dbps, %d audio : %s]\n",
- vc->bit_rate,
- audio_profile, basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- }
- break;
- }
- if( !m.dlna_pn )
- break;
- switch( audio_profile )
- {
- case PROFILE_AUDIO_MP3:
- off += sprintf(m.dlna_pn+off, "MPEG1_L3");
- break;
- case PROFILE_AUDIO_AC3:
- off += sprintf(m.dlna_pn+off, "AC3");
- break;
- case PROFILE_AUDIO_AAC:
- case PROFILE_AUDIO_AAC_MULT5:
- off += sprintf(m.dlna_pn+off, "AAC_MULT5");
- break;
- default:
- DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for %s file [%s]\n",
- m.dlna_pn, basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- break;
- }
- if( !m.dlna_pn )
- break;
- ts = ctx->priv_data;
- if( ts->packet_size == 192 )
- {
- if( vc->profile == FF_PROFILE_H264_HIGH ||
- dlna_timestamp_is_present(path) )
- ts_timestamp = VALID;
- else
- ts_timestamp = EMPTY;
- }
- switch( ts_timestamp )
- {
- case NONE:
- off += sprintf(m.dlna_pn+off, "_ISO");
- break;
- case VALID:
- off += sprintf(m.dlna_pn+off, "_T");
- case EMPTY:
- asprintf(&m.mime, "video/vnd.dlna.mpeg-tts");
- default:
- break;
- }
- }
- else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
- {
- off += sprintf(m.dlna_pn+off, "MP4_");
-
- switch( vc->profile ) {
- case FF_PROFILE_H264_BASELINE:
- if( vc->width <= 352 &&
- vc->height <= 288 )
- {
- if( ctx->bit_rate < 600000 )
- off += sprintf(m.dlna_pn+off, "BL_CIF15_");
- else if( ctx->bit_rate < 5000000 )
- off += sprintf(m.dlna_pn+off, "BL_CIF30_");
- else
- goto mp4_mp_fallback;
-
- if( audio_profile == PROFILE_AUDIO_AMR )
- {
- off += sprintf(m.dlna_pn+off, "AMR");
- }
- else if( audio_profile == PROFILE_AUDIO_AAC )
- {
- off += sprintf(m.dlna_pn+off, "AAC_");
- if( ctx->bit_rate < 520000 )
- {
- off += sprintf(m.dlna_pn+off, "520");
- }
- else if( ctx->bit_rate < 940000 )
- {
- off += sprintf(m.dlna_pn+off, "940");
- }
- else
- {
- off -= 13;
- goto mp4_mp_fallback;
- }
- }
- else
- {
- off -= 9;
- goto mp4_mp_fallback;
- }
- }
- else if( vc->width <= 720 &&
- vc->height <= 576 )
- {
- if( vc->level == 30 &&
- audio_profile == PROFILE_AUDIO_AAC &&
- ctx->bit_rate <= 5000000 )
- off += sprintf(m.dlna_pn+off, "BL_L3L_SD_AAC");
- else if( vc->level <= 31 &&
- audio_profile == PROFILE_AUDIO_AAC &&
- ctx->bit_rate <= 15000000 )
- off += sprintf(m.dlna_pn+off, "BL_L31_HD_AAC");
- else
- goto mp4_mp_fallback;
- }
- else if( vc->width <= 1280 &&
- vc->height <= 720 )
- {
- if( vc->level <= 31 &&
- audio_profile == PROFILE_AUDIO_AAC &&
- ctx->bit_rate <= 15000000 )
- off += sprintf(m.dlna_pn+off, "BL_L31_HD_AAC");
- else if( vc->level <= 32 &&
- audio_profile == PROFILE_AUDIO_AAC &&
- ctx->bit_rate <= 21000000 )
- off += sprintf(m.dlna_pn+off, "BL_L32_HD_AAC");
- else
- goto mp4_mp_fallback;
- }
- else
- goto mp4_mp_fallback;
- break;
- case FF_PROFILE_H264_MAIN:
- mp4_mp_fallback:
- off += sprintf(m.dlna_pn+off, "MP_");
- /* AVC MP4 SD profiles - 10 Mbps max */
- if( vc->width <= 720 &&
- vc->height <= 576 &&
- vc->bit_rate <= 10000000 )
- {
- sprintf(m.dlna_pn+off, "SD_");
- if( audio_profile == PROFILE_AUDIO_AC3 )
- off += sprintf(m.dlna_pn+off, "AC3");
- else if( audio_profile == PROFILE_AUDIO_AAC ||
- audio_profile == PROFILE_AUDIO_AAC_MULT5 )
- off += sprintf(m.dlna_pn+off, "AAC_MULT5");
- else if( audio_profile == PROFILE_AUDIO_MP3 )
- off += sprintf(m.dlna_pn+off, "MPEG1_L3");
- else
- m.dlna_pn[10] = '\0';
- }
- else if( vc->width <= 1280 &&
- vc->height <= 720 &&
- vc->bit_rate <= 15000000 &&
- audio_profile == PROFILE_AUDIO_AAC )
- {
- off += sprintf(m.dlna_pn+off, "HD_720p_AAC");
- }
- else if( vc->width <= 1920 &&
- vc->height <= 1080 &&
- vc->bit_rate <= 21000000 &&
- audio_profile == PROFILE_AUDIO_AAC )
- {
- off += sprintf(m.dlna_pn+off, "HD_1080i_AAC");
- }
- if( strlen(m.dlna_pn) <= 11 )
- {
- DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for %s file %s\n",
- m.dlna_pn, basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- }
- break;
- case FF_PROFILE_H264_HIGH:
- if( vc->width <= 1920 &&
- vc->height <= 1080 &&
- vc->bit_rate <= 25000000 &&
- audio_profile == PROFILE_AUDIO_AAC )
- {
- off += sprintf(m.dlna_pn+off, "HP_HD_AAC");
- }
- break;
- default:
- DPRINTF(E_DEBUG, L_METADATA, "AVC profile [%d] not recognized for file %s\n",
- vc->profile, basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- break;
- }
- }
- else
- {
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- }
- if( m.dlna_pn )
- sprintf(m.dlna_pn+off, ";%s", dlna_no_conv);
- DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is h.264\n", video_stream, basename(path));
- break;
- case CODEC_ID_MPEG4:
- fourcc[0] = vc->codec_tag & 0xff;
- fourcc[1] = vc->codec_tag>>8 & 0xff;
- fourcc[2] = vc->codec_tag>>16 & 0xff;
- fourcc[3] = vc->codec_tag>>24 & 0xff;
- DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is MPEG4 [%c%c%c%c/0x%X]\n",
- video_stream, basename(path),
- isprint(fourcc[0]) ? fourcc[0] : '_',
- isprint(fourcc[1]) ? fourcc[1] : '_',
- isprint(fourcc[2]) ? fourcc[2] : '_',
- isprint(fourcc[3]) ? fourcc[3] : '_',
- vc->codec_tag);
-
- if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
- {
- m.dlna_pn = malloc(128);
- off = sprintf(m.dlna_pn, "MPEG4_P2_");
-
- if( ends_with(path, ".3gp") )
- {
- asprintf(&m.mime, "video/3gpp");
- switch( audio_profile )
- {
- case PROFILE_AUDIO_AAC:
- off += sprintf(m.dlna_pn+off, "3GPP_SP_L0B_AAC");
- break;
- case PROFILE_AUDIO_AMR:
- off += sprintf(m.dlna_pn+off, "3GPP_SP_L0B_AMR");
- break;
- default:
- DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for MPEG4-P2 3GP/0x%X file %s\n",
- ac->codec_id, basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- break;
- }
- }
- else
- {
- if( ctx->bit_rate <= 1000000 &&
- audio_profile == PROFILE_AUDIO_AAC )
- {
- off += sprintf(m.dlna_pn+off, "MP4_ASP_AAC");
- }
- else if( ctx->bit_rate <= 4000000 &&
- vc->width <= 640 &&
- vc->height <= 480 &&
- audio_profile == PROFILE_AUDIO_AAC )
- {
- off += sprintf(m.dlna_pn+off, "MP4_SP_VGA_AAC");
- }
- else
- {
- DPRINTF(E_DEBUG, L_METADATA, "Unsupported h.264 video profile! [%dx%d, %dbps]\n",
- vc->width,
- vc->height,
- ctx->bit_rate);
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- }
- }
- if( m.dlna_pn )
- sprintf(m.dlna_pn+off, ";%s", dlna_no_conv);
- }
- break;
- case CODEC_ID_WMV3:
- /* I'm not 100% sure this is correct, but it works on everything I could get my hands on */
- if( vc->extradata_size > 0 )
- {
- if( !((vc->extradata[0] >> 3) & 1) )
- vc->level = 0;
- if( !((vc->extradata[0] >> 6) & 1) )
- vc->profile = 0;
- }
- case CODEC_ID_VC1:
- if( strcmp(ctx->iformat->name, "asf") != 0 )
- {
- DPRINTF(E_DEBUG, L_METADATA, "Skipping DLNA parsing for non-ASF VC1 file %s\n", path);
- break;
- }
- m.dlna_pn = malloc(64);
- off = sprintf(m.dlna_pn, "WMV");
- DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is VC1\n", video_stream, basename(path));
- asprintf(&m.mime, "video/x-ms-wmv");
- if( (vc->width <= 176) &&
- (vc->height <= 144) &&
- (vc->level == 0) )
- {
- off += sprintf(m.dlna_pn+off, "SPLL_");
- switch( audio_profile )
- {
- case PROFILE_AUDIO_MP3:
- off += sprintf(m.dlna_pn+off, "MP3");
- break;
- case PROFILE_AUDIO_WMA_BASE:
- off += sprintf(m.dlna_pn+off, "BASE");
- break;
- default:
- DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVSPLL/0x%X file %s\n", audio_profile, basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- break;
- }
- }
- else if( (vc->width <= 352) &&
- (vc->height <= 288) &&
- (vc->profile == 0) &&
- (ctx->bit_rate/8 <= 384000) )
- {
- off += sprintf(m.dlna_pn+off, "SPML_");
- switch( audio_profile )
- {
- case PROFILE_AUDIO_MP3:
- off += sprintf(m.dlna_pn+off, "MP3");
- break;
- case PROFILE_AUDIO_WMA_BASE:
- off += sprintf(m.dlna_pn+off, "BASE");
- break;
- default:
- DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVSPML/0x%X file %s\n", audio_profile, basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- break;
- }
- }
- else if( (vc->width <= 720) &&
- (vc->height <= 576) &&
- (ctx->bit_rate/8 <= 10000000) )
- {
- off += sprintf(m.dlna_pn+off, "MED_");
- switch( audio_profile )
- {
- case PROFILE_AUDIO_WMA_PRO:
- off += sprintf(m.dlna_pn+off, "PRO");
- break;
- case PROFILE_AUDIO_WMA_FULL:
- off += sprintf(m.dlna_pn+off, "FULL");
- break;
- case PROFILE_AUDIO_WMA_BASE:
- off += sprintf(m.dlna_pn+off, "BASE");
- break;
- default:
- DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVMED/0x%X file %s\n", audio_profile, basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- break;
- }
- }
- else if( (vc->width <= 1920) &&
- (vc->height <= 1080) &&
- (ctx->bit_rate/8 <= 20000000) )
- {
- off += sprintf(m.dlna_pn+off, "HIGH_");
- switch( audio_profile )
- {
- case PROFILE_AUDIO_WMA_PRO:
- off += sprintf(m.dlna_pn+off, "PRO");
- break;
- case PROFILE_AUDIO_WMA_FULL:
- off += sprintf(m.dlna_pn+off, "FULL");
- break;
- default:
- DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVHIGH/0x%X file %s\n", audio_profile, basename(path));
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- break;
- }
- }
- if( m.dlna_pn )
- sprintf(m.dlna_pn+off, ";%s", dlna_no_conv);
- break;
- case CODEC_ID_MSMPEG4V3:
- asprintf(&m.mime, "video/x-msvideo");
- default:
- DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s [type %d]\n", video_stream, basename(path), m.resolution, vc->codec_id);
- break;
- }
- }
- if( !m.mime )
- {
- if( strcmp(ctx->iformat->name, "avi") == 0 )
- asprintf(&m.mime, "video/x-msvideo");
- else if( strncmp(ctx->iformat->name, "mpeg", 4) == 0 )
- asprintf(&m.mime, "video/mpeg");
- else if( strcmp(ctx->iformat->name, "asf") == 0 )
- asprintf(&m.mime, "video/x-ms-wmv");
- else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
- if( ends_with(path, ".mov") )
- asprintf(&m.mime, "video/quicktime");
- else
- asprintf(&m.mime, "video/mp4");
- else if( strncmp(ctx->iformat->name, "matroska", 8) == 0 )
- asprintf(&m.mime, "video/x-matroska");
- else if( strcmp(ctx->iformat->name, "flv") == 0 )
- asprintf(&m.mime, "video/x-flv");
- else
- DPRINTF(E_WARN, L_METADATA, "%s: Unhandled format: %s\n", path, ctx->iformat->name);
- }
-
- if( strcmp(ctx->iformat->name, "asf") == 0 )
- {
- if( readtags((char *)path, &video, &file, "en_US", "asf") == 0 )
- {
- if( video.title && *video.title )
- {
- m.title = escape_tag(trim(video.title), 1);
- }
- if( video.genre && *video.genre )
- {
- m.genre = escape_tag(trim(video.genre), 1);
- }
- if( video.contributor[ROLE_TRACKARTIST] && *video.contributor[ROLE_TRACKARTIST] )
- {
- m.artist = escape_tag(trim(video.contributor[ROLE_TRACKARTIST]), 1);
- }
- if( video.contributor[ROLE_ALBUMARTIST] && *video.contributor[ROLE_ALBUMARTIST] )
- {
- m.creator = escape_tag(trim(video.contributor[ROLE_ALBUMARTIST]), 1);
- }
- else
- {
- m.creator = m.artist;
- free_flags &= ~FLAG_CREATOR;
- }
- }
- }
- #ifndef NETGEAR
- #if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
- else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 )
- {
- if( ctx->metadata )
- {
- AVMetadataTag *tag = NULL;
-
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, "Metadata:\n");
- while( (tag = av_metadata_get(ctx->metadata, "", tag, AV_METADATA_IGNORE_SUFFIX)) )
- {
- //DEBUG DPRINTF(E_DEBUG, L_METADATA, " %-16s: %s\n", tag->key, tag->value);
- if( strcmp(tag->key, "title") == 0 )
- m.title = escape_tag(trim(tag->value), 1);
- else if( strcmp(tag->key, "genre") == 0 )
- m.genre = escape_tag(trim(tag->value), 1);
- else if( strcmp(tag->key, "artist") == 0 )
- m.artist = escape_tag(trim(tag->value), 1);
- else if( strcmp(tag->key, "comment") == 0 )
- m.comment = escape_tag(trim(tag->value), 1);
- }
- }
- }
- #endif
- #endif
-video_no_dlna:
- av_close_input_file(ctx);
-
-#ifdef TIVO_SUPPORT
- if( ends_with(path, ".TiVo") && is_tivo_file(path) )
- {
- if( m.dlna_pn )
- {
- free(m.dlna_pn);
- m.dlna_pn = NULL;
- }
- m.mime = realloc(m.mime, 18);
- strcpy(m.mime, "video/x-tivo-mpeg");
- }
-#endif
- if( !m.title )
- m.title = strdup(name);
-
- album_art = find_album_art(path, video.image, video.image_size);
- freetags(&video);
-
- ret = sql_exec(db, "INSERT into DETAILS"
- " (PATH, SIZE, TIMESTAMP, DURATION, DATE, CHANNELS, BITRATE, SAMPLERATE, RESOLUTION,"
- " TITLE, CREATOR, ARTIST, GENRE, COMMENT, DLNA_PN, MIME, ALBUM_ART) "
- "VALUES"
- " (%Q, %lld, %ld, %Q, %Q, %Q, %Q, %Q, %Q, '%q', %Q, %Q, %Q, %Q, %Q, '%q', %lld);",
- path, file.st_size, file.st_mtime, m.duration,
- m.date, m.channels, m.bitrate, m.frequency, m.resolution,
- m.title, m.creator, m.artist, m.genre, m.comment, m.dlna_pn,
- m.mime, album_art);
- if( ret != SQLITE_OK )
- {
- fprintf(stderr, "Error inserting details for '%s'!\n", path);
+ //DEBUG printf(" * size: %d\n", size);
+
+ sql = sqlite3_mprintf( "INSERT into DETAILS"
+ " (TITLE, SIZE, MIME) "
+ "VALUES"
+ " ('%q', %d, %Q);",
+ name, size, "video/mpeg");
+ //DEBUG printf("SQL: %s\n", sql);
+ if( sqlite3_exec(db, sql, 0, 0, &zErrMsg) != SQLITE_OK )
+ {
+ fprintf(stderr, "Error inserting details for '%s'! [%s]\n", path, zErrMsg);
+ if (zErrMsg)
+ sqlite3_free(zErrMsg);
ret = 0;
}
else
{
ret = sqlite3_last_insert_rowid(db);
- check_for_captions(path, ret);
}
- free_metadata(&m, free_flags);
-
+ sqlite3_free(sql);
return ret;
}
diff --git a/metadata.h b/metadata.h
index 5e9344d..4c2eff2 100644
--- a/metadata.h
+++ b/metadata.h
@@ -3,98 +3,16 @@
* Project : minidlna
* Website : http://sourceforge.net/projects/minidlna/
* Author : Justin Maggard
- *
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- */
+ * Copyright (c) 2008 Justin Maggard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
#ifndef __METADATA_H__
#define __METADATA_H__
-typedef struct metadata_s {
- char *title;
- char *artist;
- char *creator;
- char *album;
- char *genre;
- char *comment;
- char *channels;
- char *bitrate;
- char *frequency;
- char *bps;
- char *resolution;
- char *duration;
- char *date;
- char *mime;
- char *dlna_pn;
-} metadata_t;
-
-typedef struct tsinfo_s {
- int x;
- int packet_size;
-} tsinfo_t;
-
-typedef enum {
- AAC_INVALID = 0,
- AAC_MAIN = 1, /* AAC Main */
- AAC_LC = 2, /* AAC Low complexity */
- AAC_SSR = 3, /* AAC SSR */
- AAC_LTP = 4, /* AAC Long term prediction */
- AAC_HE = 5, /* AAC High efficiency (SBR) */
- AAC_SCALE = 6, /* Scalable */
- AAC_TWINVQ = 7, /* TwinVQ */
- AAC_CELP = 8, /* CELP */
- AAC_HVXC = 9, /* HVXC */
- AAC_TTSI = 12, /* TTSI */
- AAC_MS = 13, /* Main synthetic */
- AAC_WAVE = 14, /* Wavetable synthesis */
- AAC_MIDI = 15, /* General MIDI */
- AAC_FX = 16, /* Algorithmic Synthesis and Audio FX */
- AAC_LC_ER = 17, /* AAC Low complexity with error recovery */
- AAC_LTP_ER = 19, /* AAC Long term prediction with error recovery */
- AAC_SCALE_ER = 20, /* AAC scalable with error recovery */
- AAC_TWINVQ_ER = 21, /* TwinVQ with error recovery */
- AAC_BSAC_ER = 22, /* BSAC with error recovery */
- AAC_LD_ER = 23, /* AAC LD with error recovery */
- AAC_CELP_ER = 24, /* CELP with error recovery */
- AAC_HXVC_ER = 25, /* HXVC with error recovery */
- AAC_HILN_ER = 26, /* HILN with error recovery */
- AAC_PARAM_ER = 27, /* Parametric with error recovery */
- AAC_SSC = 28, /* AAC SSC */
- AAC_HE_L3 = 31, /* Reserved : seems to be HeAAC L3 */
-} aac_object_type_t;
-
-typedef enum {
- NONE,
- EMPTY,
- VALID
-} ts_timestamp_t;
-
-int
-ends_with(const char * haystack, const char * needle);
-
char *
modifyString(char * string, const char * before, const char * after, short like);
-void
-check_for_captions(const char * path, sqlite_int64 detailID);
-
-sqlite_int64
-GetFolderMetadata(const char * name, const char * path, const char * artist, const char * genre, const char * album_art);
-
sqlite_int64
GetAudioMetadata(const char * path, char * name);
diff --git a/minidlna.c b/minidlna.c
index 4f37349..a9f02b4 100644
--- a/minidlna.c
+++ b/minidlna.c
@@ -1,50 +1,12 @@
/* MiniDLNA project
*
* http://sourceforge.net/projects/minidlna/
+ * (c) 2008 Justin Maggard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution
*
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- *
- * Portions of the code from the MiniUPnP project:
- *
- * Copyright (c) 2006-2007, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
+ * Portions of the code (c) Thomas Bernard, subject to
+ * the conditions detailed in the LICENSE.miniupnpd file.
*/
#include <stdlib.h>
#include <unistd.h>
@@ -57,46 +19,43 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/file.h>
+#include <syslog.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include <sys/param.h>
-#include <errno.h>
-#include <pthread.h>
-#include <pwd.h>
+#if defined(sun)
+#include <kstat.h>
+#else
+/* for BSD's sysctl */
+#include <sys/sysctl.h>
+#endif
-#include "config.h"
+#include <sqlite3.h>
-#ifdef ENABLE_NLS
-#include <libintl.h>
-#endif
+/* unix sockets */
+#include "config.h"
#include "upnpglobalvars.h"
-#include "sql.h"
#include "upnphttp.h"
#include "upnpdescgen.h"
-#include "minidlnapath.h"
+#include "miniupnpdpath.h"
#include "getifaddr.h"
#include "upnpsoap.h"
#include "options.h"
-#include "utils.h"
#include "minissdp.h"
-#include "minidlnatypes.h"
+#include "miniupnpdtypes.h"
#include "daemonize.h"
#include "upnpevents.h"
#include "scanner.h"
-#include "inotify.h"
-#include "log.h"
-#ifdef TIVO_SUPPORT
-#include "tivo_beacon.h"
-#include "tivo_utils.h"
-#endif
+#include "commonrdr.h"
+
+/* MAX_LAN_ADDR : maximum number of interfaces
+ * to listen to SSDP traffic */
+/*#define MAX_LAN_ADDR (4)*/
+
+static volatile int quitting = 0;
-#if SQLITE_VERSION_NUMBER < 3005001
-# warning "Your SQLite3 library appears to be too old! Please use 3.5.1 or newer."
-# define sqlite3_threadsafe() 0
-#endif
-
/* OpenAndConfHTTPSocket() :
* setup the socket used to handle incoming HTTP connections. */
static int
@@ -106,18 +65,15 @@ OpenAndConfHTTPSocket(unsigned short port)
int i = 1;
struct sockaddr_in listenname;
- /* Initialize client type cache */
- memset(&clients, 0, sizeof(struct client_cache_s));
-
if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
- DPRINTF(E_ERROR, L_GENERAL, "socket(http): %s\n", strerror(errno));
+ syslog(LOG_ERR, "socket(http): %m");
return -1;
}
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
{
- DPRINTF(E_WARN, L_GENERAL, "setsockopt(http, SO_REUSEADDR): %s\n", strerror(errno));
+ syslog(LOG_WARNING, "setsockopt(http, SO_REUSEADDR): %m");
}
memset(&listenname, 0, sizeof(struct sockaddr_in));
@@ -127,14 +83,14 @@ OpenAndConfHTTPSocket(unsigned short port)
if(bind(s, (struct sockaddr *)&listenname, sizeof(struct sockaddr_in)) < 0)
{
- DPRINTF(E_ERROR, L_GENERAL, "bind(http): %s\n", strerror(errno));
+ syslog(LOG_ERR, "bind(http): %m");
close(s);
return -1;
}
if(listen(s, 6) < 0)
{
- DPRINTF(E_ERROR, L_GENERAL, "listen(http): %s\n", strerror(errno));
+ syslog(LOG_ERR, "listen(http): %m");
close(s);
return -1;
}
@@ -150,7 +106,7 @@ sigterm(int sig)
/*int save_errno = errno;*/
signal(sig, SIG_IGN); /* Ignore this signal while we are quitting */
- DPRINTF(E_WARN, L_GENERAL, "received signal %d, good-bye\n", sig);
+ syslog(LOG_NOTICE, "received signal %d, good-bye", sig);
quitting = 1;
/*errno = save_errno;*/
@@ -158,11 +114,45 @@ sigterm(int sig)
/* record the startup time, for returning uptime */
static void
-set_startup_time(void)
+set_startup_time(int sysuptime)
{
startup_time = time(NULL);
+ if(sysuptime)
+ {
+ /* use system uptime instead of daemon uptime */
+ char buff[64];
+ int uptime, fd;
+ fd = open("/proc/uptime", O_RDONLY);
+ if(fd < 0)
+ {
+ syslog(LOG_ERR, "open(\"/proc/uptime\" : %m");
+ }
+ else
+ {
+ memset(buff, 0, sizeof(buff));
+ read(fd, buff, sizeof(buff) - 1);
+ uptime = atoi(buff);
+ syslog(LOG_INFO, "system uptime is %d seconds", uptime);
+ close(fd);
+ startup_time -= uptime;
+ }
+ }
}
+/* structure containing variables used during "main loop"
+ * that are filled during the init */
+struct runtime_vars {
+ /* LAN IP addresses for SSDP traffic and HTTP */
+ /* moved to global vars */
+ /*int n_lan_addr;*/
+ /*struct lan_addr_s lan_addr[MAX_LAN_ADDR];*/
+ int port; /* HTTP Port */
+ int notify_interval; /* seconds between SSDP announces */
+ /* unused rules cleaning related variables : */
+ int clean_ruleset_threshold; /* threshold for removing unused rules */
+ int clean_ruleset_interval; /* (minimum) interval between checks */
+};
+
/* parselanaddr()
* parse address with mask
* ex: 192.168.1.1/24
@@ -187,164 +177,58 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str)
}
if(n>15)
{
- DPRINTF(E_OFF, L_GENERAL, "Error parsing address/mask: %s\n", str);
+ fprintf(stderr, "Error parsing address/mask : %s\n", str);
return -1;
}
memcpy(lan_addr->str, str, n);
lan_addr->str[n] = '\0';
if(!inet_aton(lan_addr->str, &lan_addr->addr))
{
- DPRINTF(E_OFF, L_GENERAL, "Error parsing address: %s\n", str);
+ fprintf(stderr, "Error parsing address/mask : %s\n", str);
return -1;
}
lan_addr->mask.s_addr = htonl(nbits ? (0xffffffff << (32 - nbits)) : 0);
- return 0;
-}
-
-static void
-getfriendlyname(char * buf, int len)
-{
- char * dot = NULL;
- char * hn = calloc(1, 256);
- int off;
-
- if( gethostname(hn, 256) == 0 )
- {
- strncpy(buf, hn, len-1);
- buf[len] = '\0';
- dot = strchr(buf, '.');
- if( dot )
- *dot = '\0';
- }
- else
- {
- strcpy(buf, "Unknown");
- }
- free(hn);
-
- off = strlen(buf);
- off += snprintf(buf+off, len-off, ": ");
-#ifdef READYNAS
- FILE * info;
- char ibuf[64], *key, *val;
- snprintf(buf+off, len-off, "ReadyNAS");
- info = fopen("/proc/sys/dev/boot/info", "r");
- if( !info )
- return;
- while( (val = fgets(ibuf, 64, info)) != NULL )
- {
- key = strsep(&val, ": \t");
- val = trim(val);
- if( strcmp(key, "model") == 0 )
- {
- snprintf(buf+off, len-off, "%s", val);
- key = strchr(val, ' ');
- if( key )
- {
- strncpy(modelnumber, key+1, MODELNUMBER_MAX_LEN);
- modelnumber[MODELNUMBER_MAX_LEN-1] = '\0';
- *key = '\0';
- }
- snprintf(modelname, MODELNAME_MAX_LEN,
- "Windows Media Connect compatible (%s)", val);
- }
- else if( strcmp(key, "serial") == 0 )
- {
- strncpy(serialnumber, val, SERIALNUMBER_MAX_LEN);
- serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0';
- if( serialnumber[0] == '\0' )
- {
- char mac_str[13];
- if( getsyshwaddr(mac_str, sizeof(mac_str)) == 0 )
- strcpy(serialnumber, mac_str);
- else
- strcpy(serialnumber, "0");
+#ifdef MULTIPLE_EXTERNAL_IP
+ while(*p && isspace(*p))
+ p++;
+ if(*p) {
+ n = 0;
+ while(p[n] && !isspace(*p))
+ n++;
+ if(n<=15) {
+ memcpy(lan_addr->ext_ip_str, p, n);
+ lan_addr->ext_ip_str[n] = '\0';
+ if(!inet_aton(lan_addr->ext_ip_str, &lan_addr->ext_ip_addr)) {
+ /* error */
+ fprintf(stderr, "Error parsing address : %s\n", lan_addr->ext_ip_str);
}
- break;
}
}
- fclose(info);
- if( strcmp(modelnumber, "NVX") == 0 )
- memcpy(pnpx_hwid+4, "01F2&amp;DEV_0101", 17);
- else if( strcmp(modelnumber, "Pro") == 0 ||
- strcmp(modelnumber, "Pro 6") == 0 ||
- strncmp(modelnumber, "Ultra 6", 7) == 0 )
- memcpy(pnpx_hwid+4, "01F2&amp;DEV_0102", 17);
- else if( strcmp(modelnumber, "Pro 2") == 0 ||
- strncmp(modelnumber, "Ultra 2", 7) == 0 )
- memcpy(pnpx_hwid+4, "01F2&amp;DEV_0103", 17);
- else if( strcmp(modelnumber, "Pro 4") == 0 ||
- strncmp(modelnumber, "Ultra 4", 7) == 0 )
- memcpy(pnpx_hwid+4, "01F2&amp;DEV_0104", 17);
- else if( strcmp(modelnumber+1, "100") == 0 )
- memcpy(pnpx_hwid+4, "01F2&amp;DEV_0105", 17);
- else if( strcmp(modelnumber+1, "200") == 0 )
- memcpy(pnpx_hwid+4, "01F2&amp;DEV_0106", 17);
-#else
- char * logname;
- logname = getenv("LOGNAME");
-#ifndef STATIC // Disable for static linking
- if( !logname )
- {
- struct passwd * pwent;
- pwent = getpwuid(getuid());
- if( pwent )
- logname = pwent->pw_name;
- }
-#endif
- snprintf(buf+off, len-off, "%s", logname?logname:"Unknown");
#endif
-}
-
-static int
-open_db(void)
-{
- char path[PATH_MAX];
- int new_db = 0;
-
- snprintf(path, sizeof(path), "%s/files.db", db_path);
- if( access(path, F_OK) != 0 )
- {
- new_db = 1;
- make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
- }
- if( sqlite3_open(path, &db) != SQLITE_OK )
- {
- DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to open sqlite database! Exiting...\n");
- }
- sqlite3_busy_timeout(db, 5000);
- sql_exec(db, "pragma page_size = 4096");
- sql_exec(db, "pragma journal_mode = OFF");
- sql_exec(db, "pragma synchronous = OFF;");
- sql_exec(db, "pragma default_cache_size = 8192;");
- return new_db;
+ return 0;
}
/* init phase :
* 1) read configuration file
* 2) read command line arguments
* 3) daemonize
- * 4) check and write pid file
- * 5) set startup time stamp
- * 6) compute presentation URL
- * 7) set signal handlers */
+ * 4) open syslog
+ * 5) check and write pid file
+ * 6) set startup time stamp
+ * 7) compute presentation URL
+ * 8) set signal handlers */
static int
-init(int argc, char * * argv)
+init(int argc, char * * argv, struct runtime_vars * v)
{
int i;
int pid;
int debug_flag = 0;
int options_flag = 0;
+ int openlog_option;
struct sigaction sa;
/*const char * logfilename = 0;*/
const char * presurl = 0;
const char * optionsfile = "/etc/minidlna.conf";
- char mac_str[13];
- char * string, * word;
- enum media_types type;
- char * path;
- char real_path[PATH_MAX];
- char ext_ip_addr[INET_ADDRSTRLEN + 3] = {'\0'};
/* first check if "-f" option is used */
for(i=2; i<argc; i++)
@@ -357,19 +241,20 @@ init(int argc, char * * argv)
}
}
- /* set up uuid based on mac address */
- if( getsyshwaddr(mac_str, sizeof(mac_str)) < 0 )
+ /*v->n_lan_addr = 0;*/
+ char ext_ip_addr[INET_ADDRSTRLEN];
+ if( (getifaddr("eth0", ext_ip_addr, INET_ADDRSTRLEN) < 0) &&
+ (getifaddr("eth1", ext_ip_addr, INET_ADDRSTRLEN) < 0) )
{
- DPRINTF(E_OFF, L_GENERAL, "No MAC address found. Falling back to generic UUID.\n");
- strcpy(mac_str, "554e4b4e4f57");
+ printf("No IP!\n");
+ return 1;
}
- strcpy(uuidvalue+5, "4d696e69-444c-164e-9d41-");
- strncat(uuidvalue, mac_str, 12);
-
- getfriendlyname(friendly_name, FRIENDLYNAME_MAX_LEN);
-
- runtime_vars.port = -1;
- runtime_vars.notify_interval = 895; /* seconds between SSDP announces */
+ if( parselanaddr(&lan_addr[n_lan_addr], ext_ip_addr) == 0 )
+ n_lan_addr++;
+ v->port = -1;
+ v->notify_interval = 30; /* seconds between SSDP announces */
+ v->clean_ruleset_threshold = 20;
+ v->clean_ruleset_interval = 0; /* interval between ruleset check. 0=disabled */
/* read options file first since
* command line arguments have final say */
@@ -385,21 +270,19 @@ init(int argc, char * * argv)
{
switch(ary_options[i].id)
{
- case UPNPIFNAME:
- if(getifaddr(ary_options[i].value, ext_ip_addr, sizeof(ext_ip_addr)) >= 0)
- {
- if( *ext_ip_addr && parselanaddr(&lan_addr[n_lan_addr], ext_ip_addr) == 0 )
- n_lan_addr++;
- }
- else
- fprintf(stderr, "Interface %s not found, ignoring.\n", ary_options[i].value);
+ case UPNPEXT_IFNAME:
+ ext_if_name = ary_options[i].value;
+ break;
+ case UPNPEXT_IP:
+ use_ext_ip_addr = ary_options[i].value;
break;
case UPNPLISTENING_IP:
- if(n_lan_addr < MAX_LAN_ADDR)
+ if(n_lan_addr < MAX_LAN_ADDR)/* if(v->n_lan_addr < MAX_LAN_ADDR)*/
{
+ /*if(parselanaddr(&v->lan_addr[v->n_lan_addr],*/
if(parselanaddr(&lan_addr[n_lan_addr],
ary_options[i].value) == 0)
- n_lan_addr++;
+ n_lan_addr++; /*v->n_lan_addr++; */
}
else
{
@@ -408,137 +291,73 @@ init(int argc, char * * argv)
}
break;
case UPNPPORT:
- runtime_vars.port = atoi(ary_options[i].value);
+ v->port = atoi(ary_options[i].value);
+ break;
+ case UPNPBITRATE_UP:
+ upstream_bitrate = strtoul(ary_options[i].value, 0, 0);
+ break;
+ case UPNPBITRATE_DOWN:
+ downstream_bitrate = strtoul(ary_options[i].value, 0, 0);
break;
case UPNPPRESENTATIONURL:
presurl = ary_options[i].value;
break;
case UPNPNOTIFY_INTERVAL:
- runtime_vars.notify_interval = atoi(ary_options[i].value);
+ v->notify_interval = atoi(ary_options[i].value);
+ break;
+ case UPNPSYSTEM_UPTIME:
+ if(strcmp(ary_options[i].value, "yes") == 0)
+ SETFLAG(SYSUPTIMEMASK); /*sysuptime = 1;*/
+ break;
+ case UPNPPACKET_LOG:
+ if(strcmp(ary_options[i].value, "yes") == 0)
+ SETFLAG(LOGPACKETSMASK); /*logpackets = 1;*/
+ break;
+ case UPNPUUID:
+ strncpy(uuidvalue+5, ary_options[i].value,
+ strlen(uuidvalue+5) + 1);
break;
case UPNPSERIAL:
strncpy(serialnumber, ary_options[i].value, SERIALNUMBER_MAX_LEN);
serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0';
break;
- case UPNPMODEL_NAME:
- strncpy(modelname, ary_options[i].value, MODELNAME_MAX_LEN);
- modelname[MODELNAME_MAX_LEN-1] = '\0';
- break;
case UPNPMODEL_NUMBER:
strncpy(modelnumber, ary_options[i].value, MODELNUMBER_MAX_LEN);
modelnumber[MODELNUMBER_MAX_LEN-1] = '\0';
break;
- case UPNPFRIENDLYNAME:
- strncpy(friendly_name, ary_options[i].value, FRIENDLYNAME_MAX_LEN);
- friendly_name[FRIENDLYNAME_MAX_LEN-1] = '\0';
+ case UPNPCLEANTHRESHOLD:
+ v->clean_ruleset_threshold = atoi(ary_options[i].value);
break;
- case UPNPMEDIADIR:
- type = ALL_MEDIA;
- char * myval = NULL;
- switch( ary_options[i].value[0] )
- {
- case 'A':
- case 'a':
- if( ary_options[i].value[0] == 'A' || ary_options[i].value[0] == 'a' )
- type = AUDIO_ONLY;
- case 'V':
- case 'v':
- if( ary_options[i].value[0] == 'V' || ary_options[i].value[0] == 'v' )
- type = VIDEO_ONLY;
- case 'P':
- case 'p':
- if( ary_options[i].value[0] == 'P' || ary_options[i].value[0] == 'p' )
- type = IMAGES_ONLY;
- myval = index(ary_options[i].value, '/');
- case '/':
- path = realpath(myval ? myval:ary_options[i].value, real_path);
- if( !path )
- path = (myval ? myval:ary_options[i].value);
- if( access(path, F_OK) != 0 )
- {
- fprintf(stderr, "Media directory not accessible! [%s]\n",
- path);
- break;
- }
- struct media_dir_s * this_dir = calloc(1, sizeof(struct media_dir_s));
- this_dir->path = strdup(path);
- this_dir->type = type;
- if( !media_dirs )
- {
- media_dirs = this_dir;
- }
- else
- {
- struct media_dir_s * all_dirs = media_dirs;
- while( all_dirs->next )
- all_dirs = all_dirs->next;
- all_dirs->next = this_dir;
- }
- break;
- default:
- fprintf(stderr, "Media directory entry not understood! [%s]\n",
- ary_options[i].value);
- break;
- }
+ case UPNPCLEANINTERVAL:
+ v->clean_ruleset_interval = atoi(ary_options[i].value);
break;
- case UPNPALBUMART_NAMES:
- for( string = ary_options[i].value; (word = strtok(string, "/")); string = NULL ) {
- struct album_art_name_s * this_name = calloc(1, sizeof(struct album_art_name_s));
- int len = strlen(word);
- if( word[len-1] == '*' )
- {
- word[len-1] = '\0';
- this_name->wildcard = 1;
- }
- this_name->name = strdup(word);
- if( !album_art_names )
- {
- album_art_names = this_name;
- }
- else
- {
- struct album_art_name_s * all_names = album_art_names;
- while( all_names->next )
- all_names = all_names->next;
- all_names->next = this_name;
- }
- }
+#ifdef USE_PF
+ case UPNPQUEUE:
+ queue = ary_options[i].value;
break;
- case UPNPDBDIR:
- path = realpath(ary_options[i].value, real_path);
- if( !path )
- path = (ary_options[i].value);
- make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
- if( access(path, F_OK) != 0 )
- {
- DPRINTF(E_FATAL, L_GENERAL, "Database path not accessible! [%s]\n", path);
- break;
- }
- strncpy(db_path, path, PATH_MAX);
+ case UPNPTAG:
+ tag = ary_options[i].value;
break;
- case UPNPLOGDIR:
- path = realpath(ary_options[i].value, real_path);
- if( !path )
- path = (ary_options[i].value);
- make_dir(path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
- if( access(path, F_OK) != 0 )
- {
- DPRINTF(E_FATAL, L_GENERAL, "Log path not accessible! [%s]\n", path);
- break;
- }
- strncpy(log_path, path, PATH_MAX);
+#endif
+#ifdef PF_ENABLE_FILTER_RULES
+ case UPNPQUICKRULES:
+ if(strcmp(ary_options[i].value, "no") == 0)
+ SETFLAG(PFNOQUICKRULESMASK);
break;
- case UPNPINOTIFY:
- if( (strcmp(ary_options[i].value, "yes") != 0) && !atoi(ary_options[i].value) )
- CLEARFLAG(INOTIFY_MASK);
+#endif
+ case UPNPSECUREMODE:
+ if(strcmp(ary_options[i].value, "yes") == 0)
+ SETFLAG(SECUREMODEMASK);
break;
- case ENABLE_TIVO:
- if( (strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value) )
- SETFLAG(TIVO_MASK);
+#ifdef ENABLE_LEASEFILE
+ case UPNPLEASEFILE:
+ lease_file = ary_options[i].value;
+ remove(lease_file);
break;
- case ENABLE_DLNA_STRICT:
- if( (strcmp(ary_options[i].value, "yes") == 0) || atoi(ary_options[i].value) )
- SETFLAG(DLNA_STRICT_MASK);
+#endif
+ case UPNPMEDIADIR:
+ strncpy(media_dir, ary_options[i].value, MEDIADIR_MAX_LEN);
+ media_dir[MEDIADIR_MAX_LEN-1] = '\0';
break;
default:
fprintf(stderr, "Unknown option in file %s\n",
@@ -546,15 +365,6 @@ init(int argc, char * * argv)
}
}
}
- if( log_path[0] == '\0' )
- {
- if( db_path[0] == '\0' )
- strncpy(log_path, DEFAULT_LOG_PATH, PATH_MAX);
- else
- strncpy(log_path, db_path, PATH_MAX);
- }
- if( db_path[0] == '\0' )
- strncpy(db_path, DEFAULT_DB_PATH, PATH_MAX);
/* command line arguments processing */
for(i=1; i<argc; i++)
@@ -563,16 +373,23 @@ init(int argc, char * * argv)
{
fprintf(stderr, "Unknown option: %s\n", argv[i]);
}
- else if(strcmp(argv[i], "--help")==0)
- {
- runtime_vars.port = 0;
- break;
- }
else switch(argv[i][1])
{
+ case 'o':
+ if(i+1 < argc)
+ use_ext_ip_addr = argv[++i];
+ else
+ fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
+ break;
case 't':
if(i+1 < argc)
- runtime_vars.notify_interval = atoi(argv[++i]);
+ v->notify_interval = atoi(argv[++i]);
+ else
+ fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
+ break;
+ case 'u':
+ if(i+1 < argc)
+ strncpy(uuidvalue+5, argv[++i], strlen(uuidvalue+5) + 1);
else
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
break;
@@ -590,12 +407,43 @@ init(int argc, char * * argv)
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
modelnumber[MODELNUMBER_MAX_LEN-1] = '\0';
break;
+ case 'U':
+ /*sysuptime = 1;*/
+ SETFLAG(SYSUPTIMEMASK);
+ break;
/*case 'l':
logfilename = argv[++i];
break;*/
+ case 'L':
+ /*logpackets = 1;*/
+ SETFLAG(LOGPACKETSMASK);
+ break;
+ case 'S':
+ SETFLAG(SECUREMODEMASK);
+ break;
+ case 'i':
+ if(i+1 < argc)
+ ext_if_name = argv[++i];
+ else
+ fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
+ break;
+#ifdef USE_PF
+ case 'q':
+ if(i+1 < argc)
+ queue = argv[++i];
+ else
+ fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
+ break;
+ case 'T':
+ if(i+1 < argc)
+ tag = argv[++i];
+ else
+ fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
+ break;
+#endif
case 'p':
if(i+1 < argc)
- runtime_vars.port = atoi(argv[++i]);
+ v->port = atoi(argv[++i]);
else
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
break;
@@ -614,60 +462,37 @@ init(int argc, char * * argv)
else
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
break;
- case 'a':
- if(i+1 < argc)
+ case 'B':
+ if(i+2<argc)
{
- int address_already_there = 0;
- int j;
- i++;
- for(j=0; j<n_lan_addr; j++)
- {
- struct lan_addr_s tmpaddr;
- parselanaddr(&tmpaddr, argv[i]);
- if(0 == strcmp(lan_addr[j].str, tmpaddr.str))
- address_already_there = 1;
- }
- if(address_already_there)
- break;
- if(n_lan_addr < MAX_LAN_ADDR)
- {
- if(parselanaddr(&lan_addr[n_lan_addr], argv[i]) == 0)
- n_lan_addr++;
- }
- else
- {
- fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n",
- MAX_LAN_ADDR, argv[i]);
- }
+ downstream_bitrate = strtoul(argv[++i], 0, 0);
+ upstream_bitrate = strtoul(argv[++i], 0, 0);
}
else
- fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
+ fprintf(stderr, "Option -%c takes two arguments.\n", argv[i][1]);
break;
- case 'i':
+ case 'a':
if(i+1 < argc)
{
int address_already_there = 0;
int j;
i++;
- if( getifaddr(argv[i], ext_ip_addr, sizeof(ext_ip_addr)) < 0 )
- {
- fprintf(stderr, "Network interface '%s' not found.\n",
- argv[i]);
- exit(-1);
- }
- for(j=0; j<n_lan_addr; j++)
+ for(j=0; j<n_lan_addr; j++)/* for(j=0; j<v->n_lan_addr; j++)*/
{
struct lan_addr_s tmpaddr;
- parselanaddr(&tmpaddr, ext_ip_addr);
+ parselanaddr(&tmpaddr, argv[i]);
+ /*if(0 == strcmp(v->lan_addr[j].str, tmpaddr.str))*/
if(0 == strcmp(lan_addr[j].str, tmpaddr.str))
address_already_there = 1;
}
if(address_already_there)
break;
- if(n_lan_addr < MAX_LAN_ADDR)
+ if(n_lan_addr < MAX_LAN_ADDR) /*if(v->n_lan_addr < MAX_LAN_ADDR)*/
{
- if(parselanaddr(&lan_addr[n_lan_addr], ext_ip_addr) == 0)
- n_lan_addr++;
+ /*v->lan_addr[v->n_lan_addr++] = argv[i];*/
+ /*if(parselanaddr(&v->lan_addr[v->n_lan_addr], argv[i]) == 0)*/
+ if(parselanaddr(&lan_addr[n_lan_addr], argv[i]) == 0)
+ n_lan_addr++; /*v->n_lan_addr++;*/
}
else
{
@@ -681,60 +506,44 @@ init(int argc, char * * argv)
case 'f':
i++; /* discarding, the config file is already read */
break;
- case 'h':
- runtime_vars.port = 0; // triggers help display
- break;
- case 'R':
- snprintf(real_path, sizeof(real_path), "rm -rf %s/files.db %s/art_cache", db_path, db_path);
- system(real_path);
- break;
- case 'V':
- printf("Version " MINIDLNA_VERSION "\n");
- exit(0);
- break;
default:
fprintf(stderr, "Unknown option: %s\n", argv[i]);
}
}
- /* If no IP was specified, try to detect one */
- if( n_lan_addr < 1 )
- {
- if( (getsysaddr(ext_ip_addr, sizeof(ext_ip_addr)) < 0) &&
- (getifaddr("eth0", ext_ip_addr, sizeof(ext_ip_addr)) < 0) &&
- (getifaddr("eth1", ext_ip_addr, sizeof(ext_ip_addr)) < 0) )
- {
- DPRINTF(E_OFF, L_GENERAL, "No IP address automatically detected!\n");
- }
- if( *ext_ip_addr && parselanaddr(&lan_addr[n_lan_addr], ext_ip_addr) == 0 )
- {
- n_lan_addr++;
- }
- }
-
- if( (n_lan_addr==0) || (runtime_vars.port<=0) )
+ if(!ext_if_name || (/*v->*/n_lan_addr==0) || v->port<=0)
{
fprintf(stderr, "Usage:\n\t"
- "%s [-d] [-f config_file]\n"
- "\t\t[-a listening_ip] [-p port]\n"
- /*"[-l logfile] " not functionnal */
- "\t\t[-s serial] [-m model_number] \n"
- "\t\t[-t notify_interval] [-P pid_filename]\n"
- "\t\t[-w url] [-R] [-V] [-h]\n"
- "\nNotes:\n\tNotify interval is in seconds. Default is 895 seconds.\n"
- "\tDefault pid file is %s.\n"
- "\tWith -d minidlna will run in debug mode (not daemonize).\n"
- "\t-w sets the presentation url. Default is http address on port 80\n"
- "\t-h displays this text\n"
- "\t-R forces a full rescan\n"
- "\t-V print the version number\n",
- argv[0], pidfilename);
+ "%s [-f config_file] [-i ext_ifname] [-o ext_ip]\n"
+ "\t\t[-a listening_ip] [-p port] [-d] [-L] [-U] [-S]\n"
+ /*"[-l logfile] " not functionnal */
+ "\t\t[-u uuid] [-s serial] [-m model_number] \n"
+ "\t\t[-t notify_interval] [-P pid_filename]\n"
+#ifdef USE_PF
+ "\t\t[-B down up] [-w url] [-q queue] [-T tag]\n"
+#else
+ "\t\t[-B down up] [-w url]\n"
+#endif
+ "\nNotes:\n\tThere can be one or several listening_ips.\n"
+ "\tNotify interval is in seconds. Default is 30 seconds.\n"
+ "\tDefault pid file is %s.\n"
+ "\tWith -d miniupnpd will run as a standard program.\n"
+ "\t-L sets packet log in pf and ipf on.\n"
+ "\t-S sets \"secure\" mode : clients can only add mappings to their own ip\n"
+ "\t-U causes miniupnpd to report system uptime instead "
+ "of daemon uptime.\n"
+ "\t-B sets bitrates reported by daemon in bits per second.\n"
+ "\t-w sets the presentation url. Default is http address on port 80\n"
+#ifdef USE_PF
+ "\t-q sets the ALTQ queue in pf.\n"
+ "\t-T sets the tag name in pf.\n"
+#endif
+ "", argv[0], pidfilename);
return 1;
}
if(debug_flag)
{
pid = getpid();
- log_init(NULL, "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=debug");
}
else
{
@@ -746,23 +555,29 @@ init(int argc, char * * argv)
#else
pid = daemonize();
#endif
- #ifdef READYNAS
- log_init("/var/log/upnp-av.log", "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn");
- #else
- if( access(db_path, F_OK) != 0 )
- make_dir(db_path, S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
- sprintf(real_path, "%s/minidlna.log", log_path);
- log_init(real_path, "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn");
- #endif
+ }
+
+ openlog_option = LOG_PID|LOG_CONS;
+ if(debug_flag)
+ {
+ openlog_option |= LOG_PERROR; /* also log on stderr */
+ }
+
+ openlog("miniupnpd", openlog_option, LOG_MINIUPNPD);
+
+ if(!debug_flag)
+ {
+ /* speed things up and ignore LOG_INFO and LOG_DEBUG */
+ setlogmask(LOG_UPTO(LOG_NOTICE));
}
if(checkforrunning(pidfilename) < 0)
{
- DPRINTF(E_ERROR, L_GENERAL, "MiniDLNA is already running. EXITING.\n");
+ syslog(LOG_ERR, "MiniUPnPd is already running. EXITING");
return 1;
}
- set_startup_time();
+ set_startup_time(GETFLAG(SYSUPTIMEMASK)/*sysuptime*/);
/* presentation url */
if(presurl)
@@ -772,30 +587,29 @@ init(int argc, char * * argv)
}
else
{
-#ifdef READYNAS
snprintf(presentationurl, PRESENTATIONURL_MAX_LEN,
- "http://%s/admin/", lan_addr[0].str);
-#else
- snprintf(presentationurl, PRESENTATIONURL_MAX_LEN,
- "http://%s:%d/", lan_addr[0].str, runtime_vars.port);
-#endif
+ "http://%s/", lan_addr[0].str);
+ /*"http://%s:%d/", lan_addr[0].str, 80);*/
+ /*"http://%s:%d/", v->lan_addr[0].str, 80);*/
}
/* set signal handler */
- signal(SIGCLD, SIG_IGN);
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = sigterm;
+
if (sigaction(SIGTERM, &sa, NULL))
{
- DPRINTF(E_FATAL, L_GENERAL, "Failed to set SIGTERM handler. EXITING.\n");
+ syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGTERM");
+ return 1;
}
if (sigaction(SIGINT, &sa, NULL))
{
- DPRINTF(E_FATAL, L_GENERAL, "Failed to set SIGINT handler. EXITING.\n");
+ syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGINT");
+ return 1;
}
if(signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
- DPRINTF(E_FATAL, L_GENERAL, "Failed to ignore SIGPIPE signals. EXITING.\n");
+ syslog(LOG_ERR, "Failed to ignore SIGPIPE signals");
}
writepidfile(pidfilename, pid);
@@ -815,159 +629,54 @@ main(int argc, char * * argv)
struct upnphttp * e = 0;
struct upnphttp * next;
fd_set readset; /* for select() */
+#ifdef ENABLE_EVENTS
fd_set writeset;
- struct timeval timeout, timeofday, lastnotifytime = {0, 0};
- time_t lastupdatetime = 0;
- int max_fd = -1;
- int last_changecnt = 0;
- short int new_db = 0;
- pid_t scanner_pid = 0;
- pthread_t inotify_thread = 0;
- struct media_dir_s *media_path, *last_path;
- struct album_art_name_s *art_names, *last_name;
-#ifdef TIVO_SUPPORT
- unsigned short int beacon_interval = 5;
- int sbeacon = -1;
- struct sockaddr_in tivo_bcast;
- struct timeval lastbeacontime = {0, 0};
-#endif
-
-#ifdef ENABLE_NLS
- setlocale(LC_MESSAGES, "");
- setlocale(LC_CTYPE, "en_US.utf8");
- DPRINTF(E_DEBUG, L_GENERAL, "Using locale dir %s\n", bindtextdomain("minidlna", getenv("TEXTDOMAINDIR")));
- textdomain("minidlna");
#endif
+ struct timeval timeout, timeofday, lasttimeofday = {0, 0};
+ int max_fd = -1;
+ struct runtime_vars v;
- if(init(argc, argv) != 0)
+ if(init(argc, argv, &v) != 0)
return 1;
-#ifdef READYNAS
- DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION ".\n");
- unlink("/ramfs/.upnp-av_scan");
-#else
- DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION " [SQLite %s].\n", sqlite3_libversion());
- if( !sqlite3_threadsafe() )
- {
- DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not threadsafe! "
- "Scanning must be finished before file serving can begin, "
- "and inotify will be disabled.\n");
- }
- if( sqlite3_libversion_number() < 3005001 )
- {
- DPRINTF(E_WARN, L_GENERAL, "SQLite library is old. Please use version 3.5.1 or newer.\n");
- }
-#endif
LIST_INIT(&upnphttphead);
- new_db = open_db();
- if( !new_db )
+ if( access("/tmp/files.db", F_OK) )
{
- updateID = sql_get_int_field(db, "SELECT UPDATE_ID from SETTINGS");
+ sqlite3_open("/tmp/files.db", &db);
+ ScanDirectory(media_dir, NULL);
+ return 0;
}
- if( sql_get_int_field(db, "pragma user_version") != DB_VERSION )
- {
- if( new_db )
- {
- DPRINTF(E_WARN, L_GENERAL, "Creating new database...\n");
- }
- else
- {
- DPRINTF(E_WARN, L_GENERAL, "Database version mismatch; need to recreate...\n");
- }
- sqlite3_close(db);
- char *cmd;
- asprintf(&cmd, "rm -rf %s/files.db %s/art_cache", db_path, db_path);
- system(cmd);
- free(cmd);
- open_db();
- if( CreateDatabase() != 0 )
- {
- DPRINTF(E_FATAL, L_GENERAL, "ERROR: Failed to create sqlite database! Exiting...\n");
- }
-#if USE_FORK
- scanning = 1;
- sqlite3_close(db);
- scanner_pid = fork();
- open_db();
- if( !scanner_pid ) // child (scanner) process
- {
- start_scanner();
- sqlite3_close(db);
- media_path = media_dirs;
- art_names = album_art_names;
- while( media_path )
- {
- free(media_path->path);
- last_path = media_path;
- media_path = media_path->next;
- free(last_path);
- }
- while( art_names )
- {
- free(art_names->name);
- last_name = art_names;
- art_names = art_names->next;
- free(last_name);
- }
- freeoptions();
- exit(EXIT_SUCCESS);
- }
-#else
- start_scanner();
-#endif
- }
- if( sqlite3_threadsafe() && sqlite3_libversion_number() >= 3005001 &&
- GETFLAG(INOTIFY_MASK) && pthread_create(&inotify_thread, NULL, start_inotify, NULL) )
+ else
{
- DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_inotify.\n");
+ sqlite3_open("/tmp/files.db", &db);
}
+
+ /* open socket for SSDP connections */
+ /*sudp = OpenAndConfSSDPReceiveSocket(v.n_lan_addr, v.lan_addr);*/
sudp = OpenAndConfSSDPReceiveSocket(n_lan_addr, lan_addr);
if(sudp < 0)
{
- DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for receiving SSDP. EXITING\n");
+ syslog(LOG_ERR, "Failed to open socket for receiving SSDP. EXITING");
+ return 1;
}
/* open socket for HTTP connections. Listen on the 1st LAN address */
- shttpl = OpenAndConfHTTPSocket(runtime_vars.port);
+ shttpl = OpenAndConfHTTPSocket(v.port);
if(shttpl < 0)
{
- DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for HTTP. EXITING\n");
+ syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING");
+ return 1;
}
- DPRINTF(E_WARN, L_GENERAL, "HTTP listening on port %d\n", runtime_vars.port);
+ syslog(LOG_NOTICE, "HTTP listening on port %d", v.port);
/* open socket for sending notifications */
if(OpenAndConfSSDPNotifySockets(snotify) < 0)
{
- DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending SSDP notify "
- "messages. EXITING\n");
- }
-
-#ifdef TIVO_SUPPORT
- if( GETFLAG(TIVO_MASK) )
- {
- DPRINTF(E_WARN, L_GENERAL, "TiVo support is enabled.\n");
- /* Add TiVo-specific randomize function to sqlite */
- if( sqlite3_create_function(db, "tivorandom", 1, SQLITE_UTF8, NULL, &TiVoRandomSeedFunc, NULL, NULL) != SQLITE_OK )
- {
- DPRINTF(E_ERROR, L_TIVO, "ERROR: Failed to add sqlite randomize function for TiVo!\n");
- }
- /* open socket for sending Tivo notifications */
- sbeacon = OpenAndConfTivoBeaconSocket();
- if(sbeacon < 0)
- {
- DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending Tivo beacon notify "
- "messages. EXITING\n");
- }
- tivo_bcast.sin_family = AF_INET;
- tivo_bcast.sin_addr.s_addr = htonl(getBcastAddress());
- tivo_bcast.sin_port = htons(2190);
- }
- else
- {
- sbeacon = -1;
+ syslog(LOG_ERR, "Failed to open sockets for sending SSDP notify "
+ "messages. EXITING");
+ return 1;
}
-#endif
SendSSDPGoodbye(snotify, n_lan_addr);
@@ -978,71 +687,37 @@ main(int argc, char * * argv)
* needed */
if(gettimeofday(&timeofday, 0) < 0)
{
- DPRINTF(E_ERROR, L_GENERAL, "gettimeofday(): %s\n", strerror(errno));
- timeout.tv_sec = runtime_vars.notify_interval;
+ syslog(LOG_ERR, "gettimeofday(): %m");
+ timeout.tv_sec = v.notify_interval;
timeout.tv_usec = 0;
}
else
{
/* the comparaison is not very precise but who cares ? */
- if(timeofday.tv_sec >= (lastnotifytime.tv_sec + runtime_vars.notify_interval))
+ if(timeofday.tv_sec >= (lasttimeofday.tv_sec + v.notify_interval))
{
SendSSDPNotifies2(snotify,
- (unsigned short)runtime_vars.port,
- (runtime_vars.notify_interval << 1)+10);
- memcpy(&lastnotifytime, &timeofday, sizeof(struct timeval));
- timeout.tv_sec = runtime_vars.notify_interval;
+ (unsigned short)v.port,
+ (v.notify_interval << 1)+10);
+ memcpy(&lasttimeofday, &timeofday, sizeof(struct timeval));
+ timeout.tv_sec = v.notify_interval;
timeout.tv_usec = 0;
}
else
{
- timeout.tv_sec = lastnotifytime.tv_sec + runtime_vars.notify_interval
+ timeout.tv_sec = lasttimeofday.tv_sec + v.notify_interval
- timeofday.tv_sec;
- if(timeofday.tv_usec > lastnotifytime.tv_usec)
+ if(timeofday.tv_usec > lasttimeofday.tv_usec)
{
- timeout.tv_usec = 1000000 + lastnotifytime.tv_usec
+ timeout.tv_usec = 1000000 + lasttimeofday.tv_usec
- timeofday.tv_usec;
timeout.tv_sec--;
}
else
{
- timeout.tv_usec = lastnotifytime.tv_usec - timeofday.tv_usec;
- }
- }
-#ifdef TIVO_SUPPORT
- if( GETFLAG(TIVO_MASK) )
- {
- if(timeofday.tv_sec >= (lastbeacontime.tv_sec + beacon_interval))
- {
- sendBeaconMessage(sbeacon, &tivo_bcast, sizeof(struct sockaddr_in), 1);
- memcpy(&lastbeacontime, &timeofday, sizeof(struct timeval));
- if( timeout.tv_sec > beacon_interval )
- {
- timeout.tv_sec = beacon_interval;
- timeout.tv_usec = 0;
- }
- /* Beacons should be sent every 5 seconds or so for the first minute,
- * then every minute or so thereafter. */
- if( beacon_interval == 5 && (timeofday.tv_sec - startup_time) > 60 )
- {
- beacon_interval = 60;
- }
- }
- else if( timeout.tv_sec > (lastbeacontime.tv_sec + beacon_interval + 1 - timeofday.tv_sec) )
- {
- timeout.tv_sec = lastbeacontime.tv_sec + beacon_interval - timeofday.tv_sec;
+ timeout.tv_usec = lasttimeofday.tv_usec - timeofday.tv_usec;
}
}
-#endif
- }
-
- if( scanning )
- {
- if( !scanner_pid || kill(scanner_pid, 0) )
- {
- scanning = 0;
- updateID++;
- }
}
/* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
@@ -1051,74 +726,61 @@ main(int argc, char * * argv)
if (sudp >= 0)
{
FD_SET(sudp, &readset);
- max_fd = MAX(max_fd, sudp);
+ max_fd = MAX( max_fd, sudp);
}
if (shttpl >= 0)
{
FD_SET(shttpl, &readset);
- max_fd = MAX(max_fd, shttpl);
- }
-#ifdef TIVO_SUPPORT
- if (sbeacon >= 0)
- {
- FD_SET(sbeacon, &readset);
- max_fd = MAX(max_fd, sbeacon);
+ max_fd = MAX( max_fd, shttpl);
}
-#endif
+
i = 0; /* active HTTP connections count */
for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)
{
if((e->socket >= 0) && (e->state <= 2))
{
FD_SET(e->socket, &readset);
- max_fd = MAX(max_fd, e->socket);
+ max_fd = MAX( max_fd, e->socket);
i++;
}
}
-#ifdef DEBUG
/* for debug */
+#ifdef DEBUG
if(i > 1)
{
- DPRINTF(E_DEBUG, L_GENERAL, "%d active incoming HTTP connections\n", i);
+ syslog(LOG_DEBUG, "%d active incoming HTTP connections", i);
}
#endif
+
+#ifdef ENABLE_EVENTS
FD_ZERO(&writeset);
upnpevents_selectfds(&readset, &writeset, &max_fd);
+#endif
+#ifdef ENABLE_EVENTS
if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0)
+#else
+ if(select(max_fd+1, &readset, 0, 0, &timeout) < 0)
+#endif
{
if(quitting) goto shutdown;
- DPRINTF(E_ERROR, L_GENERAL, "select(all): %s\n", strerror(errno));
- DPRINTF(E_FATAL, L_GENERAL, "Failed to select open sockets. EXITING\n");
+ syslog(LOG_ERR, "select(all): %m");
+ syslog(LOG_ERR, "Failed to select open sockets. EXITING");
+ return 1; /* very serious cause of error */
}
+#ifdef ENABLE_EVENTS
upnpevents_processfds(&readset, &writeset);
+#endif
/* process SSDP packets */
if(sudp >= 0 && FD_ISSET(sudp, &readset))
{
- /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/
- ProcessSSDPRequest(sudp, (unsigned short)runtime_vars.port);
- }
-#ifdef TIVO_SUPPORT
- if(sbeacon >= 0 && FD_ISSET(sbeacon, &readset))
- {
- /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/
- ProcessTiVoBeacon(sbeacon);
- }
-#endif
- /* increment SystemUpdateID if the content database has changed,
- * and if there is an active HTTP connection, at most once every 2 seconds */
- if( i && (timeofday.tv_sec >= (lastupdatetime + 2)) )
- {
- if( scanning || sqlite3_total_changes(db) != last_changecnt )
- {
- updateID++;
- last_changecnt = sqlite3_total_changes(db);
- upnp_event_var_change_notify(EContentDirectory);
- lastupdatetime = timeofday.tv_sec;
- }
+ /*syslog(LOG_INFO, "Received UDP Packet");*/
+ /*ProcessSSDPRequest(sudp, v.lan_addr, v.n_lan_addr,*/
+ ProcessSSDPRequest(sudp, (unsigned short)v.port);
}
/* process active HTTP connections */
+ /* LIST_FOREACH macro is not available under linux */
for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)
{
if( (e->socket >= 0) && (e->state <= 2)
@@ -1137,16 +799,16 @@ main(int argc, char * * argv)
shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen);
if(shttp<0)
{
- DPRINTF(E_ERROR, L_GENERAL, "accept(http): %s\n", strerror(errno));
+ syslog(LOG_ERR, "accept(http): %m");
}
else
{
struct upnphttp * tmp = 0;
- DPRINTF(E_DEBUG, L_GENERAL, "HTTP connection from %s:%d\n",
+ syslog(LOG_INFO, "HTTP connection from %s:%d",
inet_ntoa(clientname.sin_addr),
ntohs(clientname.sin_port) );
/*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) {
- DPRINTF(E_ERROR, L_GENERAL, "fcntl F_SETFL, O_NONBLOCK\n");
+ syslog(LOG_ERR, "fcntl F_SETFL, O_NONBLOCK");
}*/
/* Create a new upnphttp object and add it to
* the active upnphttp object list */
@@ -1158,7 +820,7 @@ main(int argc, char * * argv)
}
else
{
- DPRINTF(E_ERROR, L_GENERAL, "New_upnphttp() failed\n");
+ syslog(LOG_ERR, "New_upnphttp() failed");
close(shttp);
}
}
@@ -1177,11 +839,6 @@ main(int argc, char * * argv)
}
shutdown:
- /* kill the scanner */
- if( scanning && scanner_pid )
- {
- kill(scanner_pid, 9);
- }
/* close out open sockets */
while(upnphttphead.lh_first != NULL)
{
@@ -1192,47 +849,24 @@ shutdown:
if (sudp >= 0) close(sudp);
if (shttpl >= 0) close(shttpl);
- #ifdef TIVO_SUPPORT
- if (sbeacon >= 0) close(sbeacon);
- #endif
if(SendSSDPGoodbye(snotify, n_lan_addr) < 0)
{
- DPRINTF(E_ERROR, L_GENERAL, "Failed to broadcast good-bye notifications\n");
+ syslog(LOG_ERR, "Failed to broadcast good-bye notifications");
}
- for(i=0; i<n_lan_addr; i++)
+ for(i=0; i<n_lan_addr; i++)/* for(i=0; i<v.n_lan_addr; i++)*/
close(snotify[i]);
- if( inotify_thread )
- pthread_join(inotify_thread, NULL);
-
- sql_exec(db, "UPDATE SETTINGS set UPDATE_ID = %u", updateID);
sqlite3_close(db);
- media_path = media_dirs;
- art_names = album_art_names;
- while( media_path )
- {
- free(media_path->path);
- last_path = media_path;
- media_path = media_path->next;
- free(last_path);
- }
- while( art_names )
- {
- free(art_names->name);
- last_name = art_names;
- art_names = art_names->next;
- free(last_name);
- }
-
if(unlink(pidfilename) < 0)
{
- DPRINTF(E_ERROR, L_GENERAL, "Failed to remove pidfile %s: %s\n", pidfilename, strerror(errno));
+ syslog(LOG_ERR, "Failed to remove pidfile %s: %m", pidfilename);
}
+ closelog();
freeoptions();
-
- exit(EXIT_SUCCESS);
+
+ return 0;
}
diff --git a/minissdp.c b/minissdp.c
index 3f14ccb..262e1a3 100644
--- a/minissdp.c
+++ b/minissdp.c
@@ -1,50 +1,23 @@
-/* $Id: minissdp.c,v 1.18 2011/05/03 06:14:25 jmaggard Exp $ */
+/* $Id$ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <errno.h>
-
+#include <syslog.h>
#include "config.h"
#include "upnpdescstrings.h"
-#include "minidlnapath.h"
+#include "miniupnpdpath.h"
#include "upnphttp.h"
#include "upnpglobalvars.h"
-#include "upnpreplyparse.h"
-#include "getifaddr.h"
#include "minissdp.h"
-#include "log.h"
/* SSDP ip/port */
#define SSDP_PORT (1900)
@@ -55,22 +28,24 @@ AddMulticastMembership(int s, in_addr_t ifaddr/*const char * ifaddr*/)
{
struct ip_mreq imr; /* Ip multicast membership */
- /* setting up imr structure */
- imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
- /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
- imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
+ /* setting up imr structure */
+ imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
+ /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
+ imr.imr_interface.s_addr = ifaddr; /*inet_addr(ifaddr);*/
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp, IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
+ syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
return -1;
- }
+ }
return 0;
}
int
OpenAndConfSSDPReceiveSocket()
+/*OpenAndConfSSDPReceiveSocket(int n_lan_addr,
+ struct lan_addr_s * lan_addr)*/
{
int s;
int i = 1;
@@ -78,29 +53,29 @@ OpenAndConfSSDPReceiveSocket()
if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "socket(udp): %s\n", strerror(errno));
+ syslog(LOG_ERR, "socket(udp): %m");
return -1;
}
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
{
- DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno));
+ syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
}
memset(&sockname, 0, sizeof(struct sockaddr_in));
- sockname.sin_family = AF_INET;
- sockname.sin_port = htons(SSDP_PORT);
+ sockname.sin_family = AF_INET;
+ sockname.sin_port = htons(SSDP_PORT);
/* NOTE : it seems it doesnt work when binding on the specific address */
- /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
- sockname.sin_addr.s_addr = htonl(INADDR_ANY);
- /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
+ /*sockname.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
+ sockname.sin_addr.s_addr = htonl(INADDR_ANY);
+ /*sockname.sin_addr.s_addr = inet_addr(ifaddr);*/
- if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
+ if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "bind(udp): %s\n", strerror(errno));
+ syslog(LOG_ERR, "bind(udp): %m");
close(s);
return -1;
- }
+ }
i = n_lan_addr;
while(i>0)
@@ -108,8 +83,8 @@ OpenAndConfSSDPReceiveSocket()
i--;
if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0)
{
- DPRINTF(E_WARN, L_SSDP,
- "Failed to add multicast membership for address %s\n",
+ syslog(LOG_WARNING,
+ "Failed to add multicast membership for address %s",
lan_addr[i].str );
}
}
@@ -130,7 +105,7 @@ OpenAndConfSSDPNotifySocket(in_addr_t addr)
if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "socket(udp_notify): %s\n", strerror(errno));
+ syslog(LOG_ERR, "socket(udp_notify): %m");
return -1;
}
@@ -138,41 +113,43 @@ OpenAndConfSSDPNotifySocket(in_addr_t addr)
if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %s\n", strerror(errno));
+ syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
close(s);
return -1;
}
if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, IP_MULTICAST_IF): %s\n", strerror(errno));
+ syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
close(s);
return -1;
}
if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "setsockopt(udp_notify, SO_BROADCAST): %s\n", strerror(errno));
+ syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
close(s);
return -1;
}
memset(&sockname, 0, sizeof(struct sockaddr_in));
- sockname.sin_family = AF_INET;
- sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
+ sockname.sin_family = AF_INET;
+ sockname.sin_addr.s_addr = addr; /*inet_addr(addr);*/
- if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
+ if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "bind(udp_notify): %s\n", strerror(errno));
+ syslog(LOG_ERR, "bind(udp_notify): %m");
close(s);
return -1;
- }
+ }
return s;
}
int
OpenAndConfSSDPNotifySockets(int * sockets)
+/*OpenAndConfSSDPNotifySockets(int * sockets,
+ struct lan_addr_s * lan_addr, int n_lan_addr)*/
{
int i, j;
for(i=0; i<n_lan_addr; i++)
@@ -212,31 +189,11 @@ USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
EXT:
*/
-static const char * const known_service_types[] =
-{
- uuidvalue,
- "upnp:rootdevice",
- "urn:schemas-upnp-org:device:MediaServer:",
- "urn:schemas-upnp-org:service:ContentDirectory:",
- "urn:schemas-upnp-org:service:ConnectionManager:",
- "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
- 0
-};
-
-static void
-_usleep(long usecs)
-{
- struct timespec sleep_time;
-
- sleep_time.tv_sec = 0;
- sleep_time.tv_nsec = usecs * 1000;
- nanosleep(&sleep_time, NULL);
-}
-
/* not really an SSDP "announce" as it is the response
* to a SSDP "M-SEARCH" */
static void
-SendSSDPAnnounce2(int s, struct sockaddr_in sockname, int st_no,
+SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
+ const char * st, int st_len, const char * suffix,
const char * host, unsigned short port)
{
int l, n;
@@ -245,7 +202,7 @@ SendSSDPAnnounce2(int s, struct sockaddr_in sockname, int st_no,
* follow guideline from document "UPnP Device Architecture 1.0"
* put in uppercase.
* DATE: is recommended
- * SERVER: OS/ver UPnP/1.0 minidlna/1.0
+ * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
* - check what to put in the 'Cache-Control' header
* */
char szTime[30];
@@ -253,29 +210,39 @@ SendSSDPAnnounce2(int s, struct sockaddr_in sockname, int st_no,
strftime(szTime, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&tTime));
l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
- "CACHE-CONTROL: max-age=%u\r\n"
+ "CACHE-CONTROL: max-age=1810\r\n"
"DATE: %s\r\n"
- "ST: %s%s\r\n"
- "USN: %s%s%s%s\r\n"
+ "Ext:\r\n"
+ "ST: %.*s%s\r\n"
+ "USN: %s::%.*s%s\r\n"
"EXT:\r\n"
- "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
+ "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
"Content-Length: 0\r\n"
"\r\n",
- (runtime_vars.notify_interval<<1)+10,
szTime,
- known_service_types[st_no], (st_no>1?"1":""),
- uuidvalue, (st_no>0?"::":""), (st_no>0?known_service_types[st_no]:""), (st_no>1?"1":""),
+ st_len, st, suffix,
+ uuidvalue, st_len, st, suffix,
host, (unsigned int)port);
- //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending M-SEARCH response:\n%s", buf);
n = sendto(s, buf, l, 0,
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
if(n < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "sendto(udp): %s\n", strerror(errno));
+ syslog(LOG_ERR, "sendto(udp): %m");
}
}
+static const char * const known_service_types[] =
+{
+ "upnp:rootdevice",
+ "urn:schemas-upnp-org:device:MediaServer:",
+ "urn:schemas-upnp-org:service:ContentDirectory:",
+ "urn:schemas-upnp-org:service:ConnectionManager:",
+ "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
+ uuidvalue,
+ 0
+};
+
static void
SendSSDPNotifies(int s, const char * host, unsigned short port,
unsigned int lifetime)
@@ -292,7 +259,7 @@ SendSSDPNotifies(int s, const char * host, unsigned short port,
for( dup=0; dup<2; dup++ )
{
if( dup )
- _usleep(200000);
+ usleep(200000);
i=0;
while(known_service_types[i])
{
@@ -301,27 +268,27 @@ SendSSDPNotifies(int s, const char * host, unsigned short port,
"HOST:%s:%d\r\n"
"CACHE-CONTROL:max-age=%u\r\n"
"LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
- "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
+ "SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
"NT:%s%s\r\n"
- "USN:%s%s%s%s\r\n"
+ "USN:%s::%s%s\r\n"
"NTS:ssdp:alive\r\n"
"\r\n",
SSDP_MCAST_ADDR, SSDP_PORT,
lifetime,
host, port,
- known_service_types[i], (i>1?"1":""),
- uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
+ known_service_types[i], (i==0?"":"1"),
+ uuidvalue, known_service_types[i], (i==0?"":"1") );
if(l>=sizeof(bufr))
{
- DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n");
+ syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
l = sizeof(bufr);
}
- //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
+ //DEBUG printf("Sending NOTIFY:\n%s", bufr);
n = sendto(s, bufr, l, 0,
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
if(n < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "sendto(udp_notify=%d, %s): %s\n", s, host, strerror(errno));
+ syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host);
}
i++;
}
@@ -337,145 +304,12 @@ SendSSDPNotifies2(int * sockets,
unsigned int lifetime)*/
{
int i;
- DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n");
for(i=0; i<n_lan_addr; i++)
{
SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime);
}
}
-void
-ParseUPnPClient(char *location)
-{
- char buf[8192];
- struct sockaddr_in dest;
- int s, n, do_headers = 0, nread = 0;
- struct timeval tv;
- char *addr, *path, *port_str;
- long port = 80;
- char *off = NULL, *p;
- int content_len = sizeof(buf);
- struct NameValueParserData xml;
- int client;
- enum client_types type = -1;
- uint32_t flags = 0;
- char *model;
-
- if (strncmp(location, "http://", 7) != 0)
- return;
- path = location + 7;
- port_str = strsep(&path, "/");
- if (!path)
- return;
- addr = strsep(&port_str, ":");
- if (port_str)
- {
- port = strtol(port_str, NULL, 10);
- if (!port)
- port = 80;
- }
-
- memset(&dest, '\0', sizeof(dest));
- if (!inet_aton(addr, &dest.sin_addr))
- return;
- /* Check if the client is already in cache */
- dest.sin_family = AF_INET;
- dest.sin_port = htons(port);
-
- s = socket(PF_INET, SOCK_STREAM, 0);
- if( s < 0 )
- return;
-
- tv.tv_sec = 0;
- tv.tv_usec = 500000;
- setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
- setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
-
- if( connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0 )
- goto close;
-
- n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n"
- "HOST: %s:%ld\r\n\r\n",
- path, addr, port);
- if( write(s, buf, n) < 1 )
- goto close;
-
- while( (n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0 )
- {
- nread += n;
- buf[nread] = '\0';
- n = nread;
- p = buf;
-
- while( !off && n-- > 0 )
- {
- if(p[0]=='\r' && p[1]=='\n' && p[2]=='\r' && p[3]=='\n')
- {
- off = p + 4;
- do_headers = 1;
- }
- p++;
- }
- if( !off )
- continue;
-
- if( do_headers )
- {
- p = buf;
- if( strncmp(p, "HTTP/", 5) != 0 )
- goto close;
- while(*p != ' ' && *p != '\t') p++;
- /* If we don't get a 200 status, ignore it */
- if( strtol(p, NULL, 10) != 200 )
- goto close;
- if( (p = strcasestr(p, "Content-Length:")) )
- content_len = strtol(p+15, NULL, 10);
- do_headers = 0;
- }
- if( buf + nread - off >= content_len )
- break;
- }
-close:
- close(s);
- if( !off )
- return;
- nread -= off - buf;
- ParseNameValue(off, nread, &xml);
- model = GetValueFromNameValueList(&xml, "modelName");
- if( model )
- {
- DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model);
- if (strstr(model, "Roku SoundBridge"))
- {
- type = ERokuSoundBridge;
- flags |= FLAG_AUDIO_ONLY;
- }
- }
-
- if( type < 0 )
- return;
- client = SearchClientCache(dest.sin_addr, 1);
- /* Add this client to the cache if it's not there already. */
- if( client < 0 )
- {
- for( client=0; client<CLIENT_CACHE_SLOTS; client++ )
- {
- if( clients[client].addr.s_addr )
- continue;
- get_remote_mac(dest.sin_addr, clients[client].mac);
- clients[client].addr = dest.sin_addr;
- DPRINTF(E_DEBUG, L_SSDP, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
- type, inet_ntoa(clients[client].addr),
- clients[client].mac[0], clients[client].mac[1], clients[client].mac[2],
- clients[client].mac[3], clients[client].mac[4], clients[client].mac[5], client);
- break;
- }
- }
- clients[client].type = type;
- clients[client].flags = flags;
- clients[client].age = time(NULL);
-}
-
/* ProcessSSDPRequest()
* process SSDP M-SEARCH requests and responds to them */
void
@@ -487,140 +321,60 @@ ProcessSSDPRequest(int s, unsigned short port)
char bufr[1500];
socklen_t len_r;
struct sockaddr_in sendername;
- int i;
- char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL, *loc = NULL, *srv = NULL;
- int man_len = 0;
+ int i, l;
+ int lan_addr_index = 0;
+ char * st = 0;
+ int st_len = 0;
len_r = sizeof(struct sockaddr_in);
- n = recvfrom(s, bufr, sizeof(bufr)-1, 0,
+ n = recvfrom(s, bufr, sizeof(bufr), 0,
(struct sockaddr *)&sendername, &len_r);
if(n < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno));
+ syslog(LOG_ERR, "recvfrom(udp): %m");
return;
}
- bufr[n] = '\0';
if(memcmp(bufr, "NOTIFY", 6) == 0)
{
- int loc_len = 0, srv_len = 0;
- //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
- for(i=0; i < n; i++)
- {
- if( bufr[i] == '*' )
- break;
- }
- if( !strcasestr(bufr+i, "HTTP/1.1") )
- {
- return;
- }
- while(i < n)
- {
- while((i < n - 2) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
- i++;
- i += 2;
- if(strncasecmp(bufr+i, "SERVER:", 7) == 0)
- {
- srv = bufr+i+7;
- srv_len = 0;
- while(*srv == ' ' || *srv == '\t') srv++;
- while(srv[srv_len]!='\r' && srv[srv_len]!='\n') srv_len++;
- }
- else if(strncasecmp(bufr+i, "LOCATION:", 9) == 0)
- {
- loc = bufr+i+9;
- loc_len = 0;
- while(*loc == ' ' || *loc == '\t') loc++;
- while(loc[loc_len]!='\r' && loc[loc_len]!='\n') loc_len++;
- loc[loc_len] = '\0';
- }
- else if(strncasecmp(bufr+i, "NTS:", 4) == 0)
- {
- man = bufr+i+4;
- man_len = 0;
- while(*man == ' ' || *man == '\t') man++;
- while(man[man_len]!='\r' && man[man_len]!='\n') man_len++;
- }
- }
- if (!loc || !srv || !man || (strncmp(man, "ssdp:alive", man_len) != 0))
- {
- return;
- }
- if (strncmp(srv, "Allegro-Software-RomPlug", 24) == 0)
- {
- /* Check if the client is already in cache */
- i = SearchClientCache(sendername.sin_addr, 1);
- if( i >= 0 && clients[i].type < EStandardDLNA150 )
- {
- clients[i].age = time(NULL);
- return;
- }
- ParseUPnPClient(loc);
- }
+ /* ignore NOTIFY packets. We could log the sender and device type */
return;
}
else if(memcmp(bufr, "M-SEARCH", 8) == 0)
{
- int st_len = 0, mx_len = 0, mx_val = 0;
- //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s", n, bufr);
- for(i=0; i < n; i++)
- {
- if( bufr[i] == '*' )
- break;
- }
- if( !strcasestr(bufr+i, "HTTP/1.1") )
- {
- return;
- }
+ i = 0;
while(i < n)
{
- while((i < n - 2) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
+ while(bufr[i] != '\r' || bufr[i+1] != '\n')
i++;
i += 2;
- if(strncasecmp(bufr+i, "ST:", 3) == 0)
+ if(strncasecmp(bufr+i, "st:", 3) == 0)
{
st = bufr+i+3;
st_len = 0;
while(*st == ' ' || *st == '\t') st++;
while(st[st_len]!='\r' && st[st_len]!='\n') st_len++;
- }
- else if(strncasecmp(bufr+i, "MX:", 3) == 0)
- {
- mx = bufr+i+3;
- mx_len = 0;
- while(*mx == ' ' || *mx == '\t') mx++;
- while(mx[mx_len]!='\r' && mx[mx_len]!='\n') mx_len++;
- mx_val = strtol(mx, &mx_end, 10);
- }
- else if(strncasecmp(bufr+i, "MAN:", 4) == 0)
- {
- man = bufr+i+4;
- man_len = 0;
- while(*man == ' ' || *man == '\t') man++;
- while(man[man_len]!='\r' && man[man_len]!='\n') man_len++;
+ /*syslog(LOG_INFO, "ST: %.*s", st_len, st);*/
+ /*j = 0;*/
+ /*while(bufr[i+j]!='\r') j++;*/
+ /*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
}
}
- /*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
+ /*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s:%d",
inet_ntoa(sendername.sin_addr),
ntohs(sendername.sin_port) );*/
if( ntohs(sendername.sin_port) <= 1024 || ntohs(sendername.sin_port) == 1900 )
{
- DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
+ syslog(LOG_INFO, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]",
inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
}
- else if( !man || (strncmp(man, "\"ssdp:discover\"", 15) != 0) )
- {
- DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MAN header %.*s]\n",
- inet_ntoa(sendername.sin_addr), man_len, man);
- }
- else if( !mx || mx == mx_end || mx_val < 0 ) {
- DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MX header %.*s]\n",
- inet_ntoa(sendername.sin_addr), mx_len, mx);
- }
- else if( st && (st_len > 0) )
+ else if(st)
{
- int l;
- int lan_addr_index = 0;
+ /* TODO : doesnt answer at once but wait for a random time */
+ syslog(LOG_INFO, "SSDP M-SEARCH from %s:%d ST: %.*s",
+ inet_ntoa(sendername.sin_addr),
+ ntohs(sendername.sin_port),
+ st_len, st);
/* find in which sub network the client is */
for(i = 0; i<n_lan_addr; i++)
{
@@ -631,28 +385,14 @@ ProcessSSDPRequest(int s, unsigned short port)
break;
}
}
- if( i == n_lan_addr )
- {
- DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
- inet_ntoa(sendername.sin_addr));
- return;
- }
- DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
- inet_ntoa(sendername.sin_addr),
- ntohs(sendername.sin_port),
- st_len, st, mx_len, mx, man_len, man);
/* Responds to request with a device as ST header */
for(i = 0; known_service_types[i]; i++)
{
- l = strlen(known_service_types[i]);
+ l = (int)strlen(known_service_types[i]);
if(l<=st_len && (0 == memcmp(st, known_service_types[i], l)))
{
- /* Check version number - must always be 1 currently. */
- if( (st[st_len-2] == ':') && (atoi(st+st_len-1) != 1) )
- break;
- _usleep(random()>>20);
SendSSDPAnnounce2(s, sendername,
- i,
+ st, st_len, "",
lan_addr[lan_addr_index].str, port);
break;
}
@@ -665,20 +405,27 @@ ProcessSSDPRequest(int s, unsigned short port)
{
l = (int)strlen(known_service_types[i]);
SendSSDPAnnounce2(s, sendername,
- i,
+ known_service_types[i], l, i==0?"":"1",
lan_addr[lan_addr_index].str, port);
}
}
+ /* responds to request by UUID value */
+ l = (int)strlen(uuidvalue);
+ if(l==st_len && (0 == memcmp(st, uuidvalue, l)))
+ {
+ SendSSDPAnnounce2(s, sendername, st, st_len, "",
+ lan_addr[lan_addr_index].str, port);
+ }
}
else
{
- DPRINTF(E_INFO, L_SSDP, "Invalid SSDP M-SEARCH from %s:%d\n",
+ syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s:%d",
inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
}
}
else
{
- DPRINTF(E_WARN, L_SSDP, "Unknown udp packet received from %s:%d\n",
+ syslog(LOG_NOTICE, "Unknown udp packet received from %s:%d",
inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
}
}
@@ -693,34 +440,34 @@ SendSSDPGoodbye(int * sockets, int n_sockets)
int i, j;
char bufr[512];
- memset(&sockname, 0, sizeof(struct sockaddr_in));
- sockname.sin_family = AF_INET;
- sockname.sin_port = htons(SSDP_PORT);
- sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
+ memset(&sockname, 0, sizeof(struct sockaddr_in));
+ sockname.sin_family = AF_INET;
+ sockname.sin_port = htons(SSDP_PORT);
+ sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
for(j=0; j<n_sockets; j++)
{
- for(i=0; known_service_types[i]; i++)
- {
- l = snprintf(bufr, sizeof(bufr),
- "NOTIFY * HTTP/1.1\r\n"
- "HOST:%s:%d\r\n"
- "NT:%s%s\r\n"
- "USN:%s%s%s%s\r\n"
- "NTS:ssdp:byebye\r\n"
- "\r\n",
- SSDP_MCAST_ADDR, SSDP_PORT,
- known_service_types[i], (i>1?"1":""),
- uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
- //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr);
- n = sendto(sockets[j], bufr, l, 0,
- (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
+ for(i=0; known_service_types[i]; i++)
+ {
+ l = snprintf(bufr, sizeof(bufr),
+ "NOTIFY * HTTP/1.1\r\n"
+ "HOST:%s:%d\r\n"
+ "NT:%s%s\r\n"
+ "USN:%s::%s%s\r\n"
+ "NTS:ssdp:byebye\r\n"
+ "\r\n",
+ SSDP_MCAST_ADDR, SSDP_PORT,
+ known_service_types[i], (i==0?"":"1"),
+ uuidvalue, known_service_types[i], (i==0?"":"1"));
+ n = sendto(sockets[j], bufr, l, 0,
+ (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
if(n < 0)
{
- DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", sockets[j], strerror(errno));
+ syslog(LOG_ERR, "sendto(udp_shutdown=%d): %m", sockets[j]);
return -1;
}
- }
+ }
}
return 0;
}
+
diff --git a/minissdp.h b/minissdp.h
index 7f79130..d56fe0e 100644
--- a/minissdp.h
+++ b/minissdp.h
@@ -1,35 +1,12 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006-2007, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006-2007 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
#ifndef __MINISSDP_H__
#define __MINISSDP_H__
-/*#include "minidlnatypes.h"*/
+/*#include "miniupnpdtypes.h"*/
int
OpenAndConfSSDPReceiveSocket();
diff --git a/miniupnpd.1 b/miniupnpd.1
new file mode 100644
index 0000000..a421232
--- /dev/null
+++ b/miniupnpd.1
@@ -0,0 +1,73 @@
+.TH miniupnpd 1
+.SH NAME
+miniupnpd \- UPnP Internet Gateway Device Daemon
+.SH SYNOPSIS
+.B miniupnpd
+[-f file] [-i interface] [-o address]
+[-a address] [-p port] [-d] [-L] [-U]
+[-u uuid] [-s serial] [-m model_number]
+[-q queue]
+[-t interval] [-P file]
+[-B down up] [-w url]
+.SH DESCRIPTION
+miniupnpd act as a UPnP Internet Gateway Device. It is designed
+to run on the gateway between the internet and a NAT'ed LAN. It provides
+an interface, as defined in the UPnP standard, for enabling
+clients on the LAN to ask for port redirections.
+.SH OPTIONS
+.TP
+.B \-f file
+load the config from file. default is /etc/miniupnpd.conf.
+.TP
+.B \-i interface
+interface used to connect to the internet.
+.TP
+.B \-o address
+address used to connect to the internet.
+default address of the interface will be used if not specified.
+.TP
+.B \-a address
+address on the LAN. -a option can by used multiple time if LAN is
+subdivised in several subnetworks.
+.TP
+.B \-p port
+port used for HTTP.
+.TP
+.B \-d
+debug mode : do not go to background, output messages on console
+and do not filter out low priority messages.
+.TP
+.B \-L
+set packet log in pf on
+.TP
+.B \-q queue
+set ALTQ queue in pf. filter rules must be enabled for this option
+to have any effect.
+.TP
+.B \-U
+report system uptime instead of daemon uptime to clients.
+.TP
+.B \-u uuid
+set the uuid of the UPnP Internet Gateway Device.
+.TP
+.B \-s serial
+serial number for the UPnP Internet Gateway Device.
+.TP
+.B \-m number
+model number for the UPnP Internet Gateway Device.
+.TP
+.B \-t interval
+SSDP notify interval in seconds :
+SSDP announce messages will be broadcasted at this interval.
+.TP
+.B \-P file
+pid file. default is /var/run/miniupnpd.pid
+.TP
+.B \-B down up
+download and upload bitrates reported to clients.
+.TP
+.B \-w url
+presentation url. default is first address on LAN, port 80.
+.SH "SEE ALSO"
+minissdpd(1) miniupnpc(3)
+.SH BUGS
diff --git a/miniupnpd.conf b/miniupnpd.conf
new file mode 100644
index 0000000..02b3f34
--- /dev/null
+++ b/miniupnpd.conf
@@ -0,0 +1,73 @@
+# WAN network interface
+ext_ifname=eth0
+# if the WAN interface has several IP addresses, you
+# can specify the one to use below
+#ext_ip=
+
+# LAN network interfaces IPs / networks
+# there can be multiple listening ips for SSDP traffic.
+# should be under the form nnn.nnn.nnn.nnn/nn
+# HTTP is available on all interfaces
+#listening_ip=192.168.0.1/24
+#listening_ip=10.1.11.65/24
+#listening_ip=192.168.10.112
+#listening_ip=
+# port for HTTP (descriptions and SOAP) traffic
+port=5555
+
+# enable UPNP support (default is yes)
+enable_upnp=yes
+
+media_dir=/opt
+#media_dir=/c/DLNA
+
+# lease file location
+#lease_file=/var/log/upnp.leases
+
+# bitrates reported by daemon in bits per second
+bitrate_up=1000000
+bitrate_down=10000000
+
+# "secure" mode : UPnP client are allowed to add mappings only
+# to their IP
+secure_mode=no
+
+# default presentation url is http address on port 80
+#presentation_url=http://www.mylan/index.php
+
+# report system uptime instead of daemon uptime
+system_uptime=no
+
+# notify interval in seconds. default is 30 seconds.
+notify_interval=900
+
+# unused rules cleaning.
+# never remove any rule before this threshold for the number
+# of redirections is exceeded. default to 20
+#clean_ruleset_threshold=10
+# clean process work interval in seconds. default to 0 (disabled).
+# a 600 seconds (10 minutes) interval makes sense
+clean_ruleset_interval=600
+
+# log packets in pf
+#packet_log=no
+
+# ALTQ queue in pf
+# filter rules must be used for this to be used.
+# compile with PF_ENABLE_FILTER_RULES (see config.h file)
+#queue=queue_name1
+
+# tag name in pf
+#tag=tag_name1
+
+# make filter rules in pf quick or not. default is yes
+# active when compiled with PF_ENABLE_FILTER_RULES (see config.h file)
+#quickrules=no
+
+# uuid : generate your own with "make genuuid"
+uuid=fc4ec57e-b051-11db-88f8-0060085db3f6
+
+# serial and model number the daemon will report to clients
+# in its XML description
+serial=12345678
+model_number=1
diff --git a/miniupnpdpath.h b/miniupnpdpath.h
new file mode 100644
index 0000000..3f29dd6
--- /dev/null
+++ b/miniupnpdpath.h
@@ -0,0 +1,47 @@
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2008 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#ifndef __MINIUPNPDPATH_H__
+#define __MINIUPNPDPATH_H__
+
+#include "config.h"
+
+/* Paths and other URLs in the miniupnpd http server */
+
+#define ROOTDESC_PATH "/rootDesc.xml"
+
+#ifdef HAS_DUMMY_SERVICE
+#define DUMMY_PATH "/dummy.xml"
+#endif
+
+#define WANCFG_PATH "/WANCfg.xml"
+#define WANCFG_CONTROLURL "/ctl/CmnIfCfg"
+#define WANCFG_EVENTURL "/evt/CmnIfCfg"
+
+#define WANIPC_PATH "/WANIPCn.xml"
+#define WANIPC_CONTROLURL "/ctl/IPConn"
+#define WANIPC_EVENTURL "/evt/IPConn"
+
+#define CONTENTDIRECTORY_PATH "/ContentDir.xml"
+#define CONTENTDIRECTORY_CONTROLURL "/ctl/ContentDir"
+#define CONTENTDIRECTORY_EVENTURL "/evt/ContentDir"
+
+#define CONNECTIONMGR_PATH "/ConnectionMgr.xml"
+#define CONNECTIONMGR_CONTROLURL "/ctl/ConnectionMgr"
+#define CONNECTIONMGR_EVENTURL "/evt/ConnectionMgr"
+
+#define X_MS_MEDIARECEIVERREGISTRAR_PATH "/X_MS_MediaReceiverRegistrar.xml"
+#define X_MS_MEDIARECEIVERREGISTRAR_CONTROLURL "/ctl/X_MS_MediaReceiverRegistrar"
+#define X_MS_MEDIARECEIVERREGISTRAR_EVENTURL "/evt/X_MS_MediaReceiverRegistrar"
+
+#ifdef ENABLE_L3F_SERVICE
+#define L3F_PATH "/L3F.xml"
+#define L3F_CONTROLURL "/ctl/L3F"
+#define L3F_EVENTURL "/evt/L3F"
+#endif
+
+#endif
+
diff --git a/miniupnpdtypes.h b/miniupnpdtypes.h
new file mode 100644
index 0000000..70ba799
--- /dev/null
+++ b/miniupnpdtypes.h
@@ -0,0 +1,23 @@
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2007 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+#ifndef __MINIUPNPDTYPES_H__
+#define __MINIUPNPDTYPES_H__
+
+#include "config.h"
+#include <netinet/in.h>
+
+/* structure for storing lan addresses
+ * with ascii representation and mask */
+struct lan_addr_s {
+ char str[16]; /* example: 192.168.0.1 */
+ struct in_addr addr, mask; /* ip/mask */
+#ifdef MULTIPLE_EXTERNAL_IP
+ char ext_ip_str[16];
+ struct in_addr ext_ip_addr;
+#endif
+};
+
+#endif
diff --git a/minixml.h b/minixml.h
index 91e5fdf..7be06db 100644
--- a/minixml.h
+++ b/minixml.h
@@ -3,32 +3,10 @@
* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
- *
- * Copyright (c) 2005, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * Copyright (c) 2005 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
#ifndef __MINIXML_H__
#define __MINIXML_H__
#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
diff --git a/options.c b/options.c
index cfca9ee..5741eea 100644
--- a/options.c
+++ b/options.c
@@ -2,36 +2,15 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* author: Ryan Wagoner
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
+#include <syslog.h>
#include "options.h"
#include "upnpglobalvars.h"
@@ -42,24 +21,37 @@ static const struct {
enum upnpconfigoptions id;
const char * name;
} optionids[] = {
- { UPNPIFNAME, "network_interface" },
+ { UPNPEXT_IFNAME, "ext_ifname" },
+ { UPNPEXT_IP, "ext_ip" },
{ UPNPLISTENING_IP, "listening_ip" },
{ UPNPPORT, "port" },
+ { UPNPBITRATE_UP, "bitrate_up" },
+ { UPNPBITRATE_DOWN, "bitrate_down" },
{ UPNPPRESENTATIONURL, "presentation_url" },
{ UPNPNOTIFY_INTERVAL, "notify_interval" },
{ UPNPSYSTEM_UPTIME, "system_uptime" },
+ { UPNPPACKET_LOG, "packet_log" },
{ UPNPUUID, "uuid"},
{ UPNPSERIAL, "serial"},
- { UPNPMODEL_NAME, "model_name"},
{ UPNPMODEL_NUMBER, "model_number"},
- { UPNPFRIENDLYNAME, "friendly_name"},
+ { UPNPCLEANTHRESHOLD, "clean_ruleset_threshold"},
+ { UPNPCLEANINTERVAL, "clean_ruleset_interval"},
+#ifdef ENABLE_NATPMP
+ { UPNPENABLENATPMP, "enable_natpmp"},
+#endif
+ { UPNPENABLE, "enable_upnp"},
+#ifdef USE_PF
+ { UPNPQUEUE, "queue"},
+ { UPNPTAG, "tag"},
+#endif
+#ifdef PF_ENABLE_FILTER_RULES
+ { UPNPQUICKRULES, "quickrules" },
+#endif
+#ifdef ENABLE_LEASEFILE
+ { UPNPLEASEFILE, "lease_file"},
+#endif
{ UPNPMEDIADIR, "media_dir"},
- { UPNPALBUMART_NAMES, "album_art_names"},
- { UPNPINOTIFY, "inotify" },
- { UPNPDBDIR, "db_dir" },
- { UPNPLOGDIR, "log_dir" },
- { ENABLE_TIVO, "enable_tivo" },
- { ENABLE_DLNA_STRICT, "strict_dlna" }
+ { UPNPSECUREMODE, "secure_mode"}
};
int
diff --git a/options.h b/options.h
index 88a517a..adb40c4 100644
--- a/options.h
+++ b/options.h
@@ -1,32 +1,10 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* author: Ryan Wagoner
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#ifndef __OPTIONS_H__
#define __OPTIONS_H__
@@ -35,24 +13,35 @@
/* enum of option available in the miniupnpd.conf */
enum upnpconfigoptions {
UPNP_INVALID = 0,
- UPNPIFNAME = 1, /* ext_ifname */
+ UPNPEXT_IFNAME = 1, /* ext_ifname */
+ UPNPEXT_IP, /* ext_ip */
UPNPLISTENING_IP, /* listening_ip */
- UPNPPORT, /* port */
- UPNPPRESENTATIONURL, /* presentation_url */
- UPNPNOTIFY_INTERVAL, /* notify_interval */
- UPNPSYSTEM_UPTIME, /* system_uptime */
- UPNPUUID, /* uuid */
- UPNPSERIAL, /* serial */
- UPNPMODEL_NAME, /* model_name */
+ UPNPPORT, /* "port" */
+ UPNPBITRATE_UP, /* "bitrate_up" */
+ UPNPBITRATE_DOWN, /* "bitrate_down" */
+ UPNPPRESENTATIONURL, /* presentation_url */
+ UPNPNOTIFY_INTERVAL, /* notify_interval */
+ UPNPSYSTEM_UPTIME, /* "system_uptime" */
+ UPNPPACKET_LOG, /* "packet_log" */
+ UPNPUUID, /* uuid */
+ UPNPSERIAL, /* serial */
UPNPMODEL_NUMBER, /* model_number */
- UPNPFRIENDLYNAME, /* how the system should show up to DLNA clients */
+ UPNPCLEANTHRESHOLD, /* clean_ruleset_threshold */
+ UPNPCLEANINTERVAL, /* clean_ruleset_interval */
+ UPNPENABLENATPMP, /* enable_natpmp */
+#ifdef USE_PF
+ UPNPQUEUE, /* queue */
+ UPNPTAG, /* tag */
+#endif
+#ifdef PF_ENABLE_FILTER_RULES
+ UPNPQUICKRULES, /* quickrules */
+#endif
+ UPNPSECUREMODE, /* secure_mode */
+#ifdef ENABLE_LEASEFILE
+ UPNPLEASEFILE, /* lease_file */
+#endif
UPNPMEDIADIR, /* directory to search for UPnP-A/V content */
- UPNPALBUMART_NAMES, /* list of '/'-delimited file names to check for album art */
- UPNPINOTIFY, /* enable inotify on the media directories */
- UPNPDBDIR, /* base directory to store the database and album art cache */
- UPNPLOGDIR, /* base directory to store the log file */
- ENABLE_TIVO, /* enable support for streaming images and music to TiVo */
- ENABLE_DLNA_STRICT /* strictly adhere to DLNA specs */
+ UPNPENABLE /* enable_upnp */
};
/* readoptionsfile()
@@ -66,7 +55,7 @@ readoptionsfile(const char * fname);
void
freeoptions(void);
-#define MAX_OPTION_VALUE_LEN (200)
+#define MAX_OPTION_VALUE_LEN (80)
struct option
{
enum upnpconfigoptions id;
diff --git a/scanner.c b/scanner.c
index 84f0b4c..b2c140e 100644
--- a/scanner.c
+++ b/scanner.c
@@ -1,19 +1,19 @@
-/* MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
+/* MiniDLNA media server
+ * Copyright (C) 2008 Justin Maggard
*
- * This file is part of MiniDLNA.
+ * 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.
*
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * 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.
*
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <string.h>
@@ -21,625 +21,417 @@
#include <unistd.h>
#include <dirent.h>
#include <locale.h>
-#include <libgen.h>
-#include <inttypes.h>
#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-#include "config.h"
-#ifdef ENABLE_NLS
-#include <libintl.h>
-#endif
#include <sqlite3.h>
#include "upnpglobalvars.h"
#include "metadata.h"
-#include "playlist.h"
-#include "utils.h"
#include "sql.h"
#include "scanner.h"
-#include "log.h"
-int valid_cache = 0;
-
-struct virtual_item
+int
+ends_with(const char * haystack, const char * needle)
{
- sqlite3_int64 objectID;
- char parentID[64];
- char name[256];
-};
+ const char *found = strcasestr(haystack, needle);
+ return (found && found[strlen(needle)] == '\0');
+}
-sqlite_int64
-get_next_available_id(const char * table, const char * parentID)
+int
+is_video(const char * file)
{
- char *ret, *base;
- sqlite_int64 objectID = 0;
-
- ret = sql_get_text_field(db, "SELECT OBJECT_ID from %s where ID = "
- "(SELECT max(ID) from %s where PARENT_ID = '%s')",
- table, table, parentID);
- if( ret )
- {
- base = strrchr(ret, '$');
- if( base )
- objectID = strtoll(base+1, NULL, 16) + 1;
- sqlite3_free(ret);
- }
+ return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
+ ends_with(file, ".ts") || ends_with(file, ".avi"));
+}
- return objectID;
+int
+is_audio(const char * file)
+{
+ return (ends_with(file, ".mp3") ||
+ ends_with(file, ".m4a") || ends_with(file, ".aac"));
}
int
-insert_container(const char * item, const char * rootParent, const char * refID, const char *class,
- const char *artist, const char *genre, const char *album_art, sqlite3_int64 *objectID, sqlite3_int64 *parentID)
+is_image(const char * file)
{
- char *result;
- char *base;
- int ret = 0;
- sqlite_int64 detailID = 0;
+ return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
+}
- result = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS"
- " where PARENT_ID = '%s'"
- " and NAME = '%q'"
- " and CLASS = 'container.%s' limit 1",
- rootParent, item, class);
- if( result )
+long long int
+insert_container(const char * tmpTable, const char * item, const char * rootParent, const char *subParent, const char *class, long unsigned int detailID)
+{
+ char **result;
+ char *sql;
+ int cols, rows, ret;
+ char *zErrMsg = NULL;
+ int parentID = 0, objectID = 0;
+
+ sql = sqlite3_mprintf("SELECT * from %s where ITEM = '%q' and SUBITEM = '%q'", tmpTable, item, subParent);
+ ret = sql_get_table(db, sql, &result, &rows, &cols, &zErrMsg);
+ sqlite3_free(sql);
+ if( cols )
{
- base = strrchr(result, '$');
- if( base )
- *parentID = strtoll(base+1, NULL, 16);
- else
- *parentID = 0;
- *objectID = get_next_available_id("OBJECTS", result);
+ sscanf(result[4], "%X", &parentID);
+ sqlite3_free_table(result);
+ sql = sqlite3_mprintf("SELECT OBJECT_ID, max(ID) from OBJECTS where PARENT_ID = '%s$%X'", rootParent, parentID);
+ ret = sql_get_table(db, sql, &result, 0, &cols, &zErrMsg);
+ sqlite3_free(sql);
+ if( result[2] && (sscanf(rindex(result[2], '$')+1, "%X", &objectID) == 1) )
+ {
+ objectID++;
+ }
}
else
{
- *objectID = 0;
- *parentID = get_next_available_id("OBJECTS", rootParent);
- if( refID )
+ sqlite3_free_table(result);
+ sql = sqlite3_mprintf("SELECT OBJECT_ID, max(ID) from OBJECTS where PARENT_ID = '%s'", rootParent);
+ sql_get_table(db, sql, &result, &rows, &cols, &zErrMsg);
+ sqlite3_free(sql);
+ if( result[2] && (sscanf(rindex(result[2], '$')+1, "%X", &parentID) == 1) )
{
- result = sql_get_text_field(db, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = %Q", refID);
- if( result )
- detailID = strtoll(result, NULL, 10);
+ parentID++;
}
- if( !detailID )
+ else
{
- detailID = GetFolderMetadata(item, NULL, artist, genre, album_art);
+ parentID = 0;
}
- ret = sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
- "VALUES"
- " ('%s$%"PRIX64"', '%s', %Q, %"PRId64", 'container.%s', '%q')",
- rootParent, *parentID, rootParent, refID, detailID, class, item);
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME) "
+ "VALUES"
+ " ('%s$%X', '%s', %lu, 'container.%s', '%q')",
+ rootParent, parentID, rootParent, detailID, class, item);
+ ret = sql_exec(db, sql);
+ sqlite3_free(sql);
+ sql = sqlite3_mprintf("INSERT into %s values ('%q', '%X', '%q')", tmpTable, item, parentID, subParent);
+ sql_exec(db, sql);
+ sqlite3_free(sql);
}
- sqlite3_free(result);
+ sqlite3_free_table(result);
- return ret;
+ return (long long)parentID<<32|objectID;
}
-static void
-insert_containers(const char * name, const char *path, const char * refID, const char * class, sqlite3_int64 detailID)
+void
+insert_containers(const char * name, const char *path, const char * refID, const char * class, long unsigned int detailID)
{
+ char sql_buf[128];
char *sql;
char **result;
int ret;
int cols, row;
- sqlite_int64 objectID, parentID;
+ char *zErrMsg = NULL;
+ long long int container;
+ int parentID;
+ int objectID = -1;
+
+ sprintf(sql_buf, "SELECT * from DETAILS where ID = %lu", detailID);
+ ret = sql_get_table(db, sql_buf, &result, &row, &cols, &zErrMsg);
if( strstr(class, "imageItem") )
{
- char *date = NULL, *cam = NULL;
- char date_taken[13], camera[64];
- static struct virtual_item last_date;
- static struct virtual_item last_cam;
- static struct virtual_item last_camdate;
- static sqlite_int64 last_all_objectID = 0;
-
- asprintf(&sql, "SELECT DATE, CREATOR from DETAILS where ID = %"PRId64, detailID);
- ret = sql_get_table(db, sql, &result, &row, &cols);
- free(sql);
- if( ret == SQLITE_OK )
- {
- date = result[2];
- cam = result[3];
- }
-
+ char *date = result[12+cols], *cam = result[16+cols];
+ char date_taken[11]; date_taken[10] = '\0';
+ static int last_all_objectID = 0;
if( date )
- {
strncpy(date_taken, date, 10);
- date_taken[10] = '\0';
- }
- else
- {
- strcpy(date_taken, _("Unknown Date"));
- }
- if( valid_cache && strcmp(last_date.name, date_taken) == 0 )
- {
- last_date.objectID++;
- //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
- }
- else
- {
- insert_container(date_taken, IMAGE_DATE_ID, NULL, "album.photoAlbum", NULL, NULL, NULL, &objectID, &parentID);
- sprintf(last_date.parentID, IMAGE_DATE_ID"$%"PRIX64, parentID);
- last_date.objectID = objectID;
- strcpy(last_date.name, date_taken);
- //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID);
- }
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
- last_date.parentID, last_date.objectID, last_date.parentID, refID, class, detailID, name);
-
- if( cam )
- {
- strncpy(camera, cam, 63);
- camera[63] = '\0';
- }
- else
- {
- strcpy(camera, _("Unknown Camera"));
- }
- if( !valid_cache || strcmp(camera, last_cam.name) != 0 )
- {
- insert_container(camera, IMAGE_CAMERA_ID, NULL, "storageFolder", NULL, NULL, NULL, &objectID, &parentID);
- sprintf(last_cam.parentID, IMAGE_CAMERA_ID"$%"PRIX64, parentID);
- strncpy(last_cam.name, camera, 255);
- last_camdate.name[0] = '\0';
- }
- if( valid_cache && strcmp(last_camdate.name, date_taken) == 0 )
+ if( date )
{
- last_camdate.objectID++;
- //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last camdate item: %s/%s/%s/%X\n", camera, last_camdate.name, last_camdate.parentID, last_camdate.objectID);
+ container = insert_container("DATES", date_taken, "3$12", NULL, "album.photoAlbum", 0);
+ parentID = container>>32;
+ objectID = container;
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
+ "VALUES"
+ " ('3$12$%X$%X', '3$12$%X', '%s', '%s', %lu, %Q, %Q)",
+ parentID, objectID, parentID, refID, class, detailID, path, name);
+ sql_exec(db, sql);
+ sqlite3_free(sql);
}
- else
+ if( cam && date )
{
- insert_container(date_taken, last_cam.parentID, NULL, "album.photoAlbum", NULL, NULL, NULL, &objectID, &parentID);
- sprintf(last_camdate.parentID, "%s$%"PRIX64, last_cam.parentID, parentID);
- last_camdate.objectID = objectID;
- strcpy(last_camdate.name, date_taken);
- //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached camdate item: %s/%s/%s/%X\n", camera, last_camdate.name, last_camdate.parentID, last_camdate.objectID);
+ container = insert_container("CAMS", cam, "3$13", NULL, "storageFolder", 0);
+ parentID = container>>32;
+ //objectID = container;
+ char parent[64];
+ sprintf(parent, "3$13$%X", parentID);
+ long long int subcontainer = insert_container("CAMDATE", date_taken, parent, cam, "storageFolder", 0);
+ int subParentID = subcontainer>>32;
+ int subObjectID = subcontainer;
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
+ "VALUES"
+ " ('3$13$%X$%X$%X', '3$13$%X$%X', '%s', '%s', %lu, %Q, %Q)",
+ parentID, subParentID, subObjectID, parentID, subParentID, refID, class, detailID, path, name);
+ sql_exec(db, sql);
+ sqlite3_free(sql);
}
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
- last_camdate.parentID, last_camdate.objectID, last_camdate.parentID, refID, class, detailID, name);
/* All Images */
- if( !last_all_objectID )
- {
- last_all_objectID = get_next_available_id("OBJECTS", IMAGE_ALL_ID);
- }
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('"IMAGE_ALL_ID"$%"PRIX64"', '"IMAGE_ALL_ID"', '%s', '%s', %"PRId64", %Q)",
- last_all_objectID++, refID, class, detailID, name);
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
+ "VALUES"
+ " ('3$11$%X', '3$11', '%s', '%s', %lu, %Q, %Q)",
+ last_all_objectID++, refID, class, detailID, path, name);
+ sql_exec(db, sql);
+ sqlite3_free(sql);
}
else if( strstr(class, "audioItem") )
{
- asprintf(&sql, "SELECT ALBUM, ARTIST, GENRE, ALBUM_ART from DETAILS where ID = %"PRId64, detailID);
- ret = sql_get_table(db, sql, &result, &row, &cols);
- free(sql);
- if( ret != SQLITE_OK )
- return;
- if( !row )
- {
- sqlite3_free_table(result);
- return;
- }
- char *album = result[4], *artist = result[5], *genre = result[6];
- char *album_art = result[7];
- static struct virtual_item last_album;
- static struct virtual_item last_artist;
- static struct virtual_item last_artistAlbum;
- static struct virtual_item last_artistAlbumAll;
- static struct virtual_item last_genre;
- static struct virtual_item last_genreArtist;
- static struct virtual_item last_genreArtistAll;
- static sqlite_int64 last_all_objectID = 0;
+ char *artist = result[6+cols], *album = result[7+cols], *genre = result[8+cols];
+ static char last_artist[1024];
+ static int last_artist_parentID, last_artist_objectID;
+ static char last_album[1024];
+ static int last_album_parentID, last_album_objectID;
+ static char last_genre[1024];
+ static int last_genre_parentID, last_genre_objectID;
+ static int last_all_objectID = 0;
- if( album )
+ if( artist )
{
- if( valid_cache && strcmp(album, last_album.name) == 0 )
+ if( strcmp(artist, last_artist) == 0 )
{
- last_album.objectID++;
- //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last album item: %s/%s/%X\n", last_album.name, last_album.parentID, last_album.objectID);
+ objectID = ++last_artist_objectID;
+ parentID = last_artist_parentID;
}
else
{
- strcpy(last_album.name, album);
- insert_container(album, MUSIC_ALBUM_ID, NULL, "album.musicAlbum", artist, genre, album_art, &objectID, &parentID);
- sprintf(last_album.parentID, MUSIC_ALBUM_ID"$%llX", parentID);
- last_album.objectID = objectID;
- //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached album item: %s/%s/%X\n", last_album.name, last_album.parentID, last_album.objectID);
+ strcpy(last_artist, artist);
+ container = insert_container("ARTISTS", artist, "1$6", NULL, "person.musicArtist", 0);
+ parentID = container>>32;
+ objectID = container;
+ last_artist_objectID = objectID;
+ last_artist_parentID = parentID;
}
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
- last_album.parentID, last_album.objectID, last_album.parentID, refID, class, detailID, name);
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
+ "VALUES"
+ " ('1$6$%X$%X', '1$6$%X', '%s', '%s', %lu, %Q, %Q)",
+ parentID, objectID, parentID, refID, class, detailID, path, name);
+ sql_exec(db, sql);
+ sqlite3_free(sql);
}
- if( artist )
+ if( album )
{
- if( !valid_cache || strcmp(artist, last_artist.name) != 0 )
+ if( strcmp(album, last_album) == 0 )
{
- insert_container(artist, MUSIC_ARTIST_ID, NULL, "person.musicArtist", NULL, genre, NULL, &objectID, &parentID);
- sprintf(last_artist.parentID, MUSIC_ARTIST_ID"$%"PRIX64, parentID);
- strcpy(last_artist.name, artist);
- last_artistAlbum.name[0] = '\0';
- /* Add this file to the "- All Albums -" container as well */
- insert_container(_("- All Albums -"), last_artist.parentID, NULL, "album", artist, genre, NULL, &objectID, &parentID);
- sprintf(last_artistAlbumAll.parentID, "%s$%"PRIX64, last_artist.parentID, parentID);
- last_artistAlbumAll.objectID = objectID;
+ objectID = ++last_album_objectID;
+ parentID = last_album_parentID;
}
else
{
- last_artistAlbumAll.objectID++;
+ strcpy(last_album, album);
+ container = insert_container("ALBUMS", album, "1$7", NULL, "album.musicAlbum", detailID);
+ parentID = container>>32;
+ objectID = container;
+ last_album_objectID = objectID;
+ last_album_parentID = parentID;
}
- if( valid_cache && strcmp(album?album:_("Unknown Album"), last_artistAlbum.name) == 0 )
- {
- last_artistAlbum.objectID++;
- //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Using last artist/album item: %s/%s/%X\n", last_artist.name, last_artist.parentID, last_artist.objectID);
- }
- else
- {
- insert_container(album?album:_("Unknown Album"), last_artist.parentID, album?last_album.parentID:NULL,
- "album.musicAlbum", artist, genre, album_art, &objectID, &parentID);
- sprintf(last_artistAlbum.parentID, "%s$%"PRIX64, last_artist.parentID, parentID);
- last_artistAlbum.objectID = objectID;
- strcpy(last_artistAlbum.name, album?album:_("Unknown Album"));
- //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached artist/album item: %s/%s/%X\n", last_artist.name, last_artist.parentID, last_artist.objectID);
- }
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
- last_artistAlbum.parentID, last_artistAlbum.objectID, last_artistAlbum.parentID, refID, class, detailID, name);
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
- last_artistAlbumAll.parentID, last_artistAlbumAll.objectID, last_artistAlbumAll.parentID, refID, class, detailID, name);
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
+ "VALUES"
+ " ('1$7$%X$%X', '1$7$%X', '%s', '%s', %lu, %Q, %Q)",
+ parentID, objectID, parentID, refID, class, detailID, path, name);
+ sql_exec(db, sql);
+ sqlite3_free(sql);
}
if( genre )
{
- if( !valid_cache || strcmp(genre, last_genre.name) != 0 )
+ if( strcmp(genre, last_genre) == 0 )
{
- insert_container(genre, MUSIC_GENRE_ID, NULL, "genre.musicGenre", NULL, NULL, NULL, &objectID, &parentID);
- sprintf(last_genre.parentID, MUSIC_GENRE_ID"$%"PRIX64, parentID);
- strcpy(last_genre.name, genre);
- last_genreArtist.name[0] = '\0';
- /* Add this file to the "- All Artists -" container as well */
- insert_container(_("- All Artists -"), last_genre.parentID, NULL, "person", NULL, genre, NULL, &objectID, &parentID);
- sprintf(last_genreArtistAll.parentID, "%s$%"PRIX64, last_genre.parentID, parentID);
- last_genreArtistAll.objectID = objectID;
+ objectID = ++last_genre_objectID;
+ parentID = last_genre_parentID;
}
else
{
- last_genreArtistAll.objectID++;
+ strcpy(last_genre, genre);
+ container = insert_container("GENRES", genre, "1$5", NULL, "genre.musicGenre", 0);
+ parentID = container>>32;
+ objectID = container;
+ last_genre_objectID = objectID;
+ last_genre_parentID = parentID;
}
- if( valid_cache && strcmp(artist?artist:_("Unknown Artist"), last_genreArtist.name) == 0 )
- {
- last_genreArtist.objectID++;
- }
- else
- {
- insert_container(artist?artist:_("Unknown Artist"), last_genre.parentID, artist?last_artist.parentID:NULL,
- "person.musicArtist", NULL, genre, NULL, &objectID, &parentID);
- sprintf(last_genreArtist.parentID, "%s$%"PRIX64, last_genre.parentID, parentID);
- last_genreArtist.objectID = objectID;
- strcpy(last_genreArtist.name, artist?artist:_("Unknown Artist"));
- //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached genre/artist item: %s/%s/%X\n", last_genreArtist.name, last_genreArtist.parentID, last_genreArtist.objectID);
- }
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
- last_genreArtist.parentID, last_genreArtist.objectID, last_genreArtist.parentID, refID, class, detailID, name);
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)",
- last_genreArtistAll.parentID, last_genreArtistAll.objectID, last_genreArtistAll.parentID, refID, class, detailID, name);
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
+ "VALUES"
+ " ('1$5$%X$%X', '1$5$%X', '%s', '%s', %lu, %Q, %Q)",
+ parentID, objectID, parentID, refID, class, detailID, path, name);
+ sql_exec(db, sql);
+ sqlite3_free(sql);
}
/* All Music */
- if( !last_all_objectID )
- {
- last_all_objectID = get_next_available_id("OBJECTS", MUSIC_ALL_ID);
- }
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('"MUSIC_ALL_ID"$%"PRIX64"', '"MUSIC_ALL_ID"', '%s', '%s', %"PRId64", %Q)",
- last_all_objectID++, refID, class, detailID, name);
- }
- else if( strstr(class, "videoItem") )
- {
- static sqlite_int64 last_all_objectID = 0;
-
- /* All Videos */
- if( !last_all_objectID )
- {
- last_all_objectID = get_next_available_id("OBJECTS", VIDEO_ALL_ID);
- }
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('"VIDEO_ALL_ID"$%"PRIX64"', '"VIDEO_ALL_ID"', '%s', '%s', %"PRId64", %Q)",
- last_all_objectID++, refID, class, detailID, name);
- return;
- }
- else
- {
- return;
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
+ "VALUES"
+ " ('1$4$%X', '1$4', '%s', '%s', %lu, %Q, %Q)",
+ last_all_objectID++, refID, class, detailID, path, name);
+ sql_exec(db, sql);
+ sqlite3_free(sql);
}
sqlite3_free_table(result);
- valid_cache = 1;
}
int
-insert_directory(const char * name, const char * path, const char * base, const char * parentID, int objectID)
+insert_directory(const char * name, const char * path, const char * parentID, int objectID)
{
- int found = 0;
- sqlite_int64 detailID = 0;
- char * refID = NULL;
+ char *sql;
+ int ret, i;
char class[] = "container.storageFolder";
- char * id_buf = NULL;
- char * parent_buf = NULL;
- char *dir_buf, *dir;
- char *result, *p;
- static char last_found[256] = "-1";
-
- if( strcmp(base, BROWSEDIR_ID) != 0 )
- asprintf(&refID, "%s%s$%X", BROWSEDIR_ID, parentID, objectID);
+ const char * const base[] = { BROWSEDIR_ID, MUSIC_DIR_ID, VIDEO_DIR_ID, IMAGE_DIR_ID, 0 };
- if( refID )
+ for( i=0; base[i]; i++ )
{
- dir_buf = strdup(path);
- dir = dirname(dir_buf);
- asprintf(&id_buf, "%s%s$%X", base, parentID, objectID);
- asprintf(&parent_buf, "%s%s", base, parentID);
- while( !found )
- {
- if( strcmp(id_buf, last_found) == 0 )
- break;
- if( sql_get_int_field(db, "SELECT count(*) from OBJECTS where OBJECT_ID = '%s'", id_buf) > 0 )
- {
- strcpy(last_found, id_buf);
- break;
- }
- /* Does not exist. Need to create, and may need to create parents also */
- result = sql_get_text_field(db, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID);
- if( result )
- {
- detailID = strtoll(result, NULL, 10);
- sqlite3_free(result);
- }
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
- "VALUES"
- " ('%s', '%s', %Q, %"PRId64", '%s', '%q')",
- id_buf, parent_buf, refID, detailID, class, strrchr(dir, '/')+1);
- if( (p = strrchr(id_buf, '$')) )
- *p = '\0';
- if( (p = strrchr(parent_buf, '$')) )
- *p = '\0';
- if( (p = strrchr(refID, '$')) )
- *p = '\0';
- dir = dirname(dir);
- }
- free(refID);
- free(parent_buf);
- free(id_buf);
- free(dir_buf);
- return 1;
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, CLASS, PATH, NAME) "
+ "VALUES"
+ " ('%s%s$%X', '%s%s', '%s', '%q', '%q')",
+ base[i], parentID, objectID, base[i], parentID, class, path, name);
+ //DEBUG printf("SQL: %s\n", sql);
+ ret = sql_exec(db, sql);
+ sqlite3_free(sql);
}
-
- detailID = GetFolderMetadata(name, path, NULL, NULL, NULL);
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) "
- "VALUES"
- " ('%s%s$%X', '%s%s', %Q, %"PRId64", '%s', '%q')",
- base, parentID, objectID, base, parentID, refID, detailID, class, name);
- if( refID )
- free(refID);
-
return -1;
}
int
insert_file(char * name, const char * path, const char * parentID, int object)
{
+ char *sql;
char class[32];
char objectID[64];
- sqlite3_int64 detailID = 0;
+ unsigned long int detailID = 0;
char base[8];
- char * typedir_parentID;
- int typedir_objectID;
- char * baseid;
- char * orig_name = NULL;
+
+ static long unsigned int fileno = 0;
+ printf("Scanned %lu files...\r", fileno++); fflush(stdout);
+
+ sprintf(objectID, "%s%s$%X", BROWSEDIR_ID, parentID, object);
if( is_image(name) )
{
- if( is_album_art(name) )
- return -1;
strcpy(base, IMAGE_DIR_ID);
- strcpy(class, "item.imageItem.photo");
+ strcpy(class, "item.imageItem");
detailID = GetImageMetadata(path, name);
}
- else if( is_video(name) )
- {
- orig_name = strdup(name);
- strcpy(base, VIDEO_DIR_ID);
- strcpy(class, "item.videoItem");
- detailID = GetVideoMetadata(path, name);
- if( !detailID )
- strcpy(name, orig_name);
- }
- else if( is_playlist(name) )
- {
- if( insert_playlist(path, name) == 0 )
- return 1;
- }
- if( !detailID && is_audio(name) )
+ else if( is_audio(name) )
{
strcpy(base, MUSIC_DIR_ID);
strcpy(class, "item.audioItem.musicTrack");
detailID = GetAudioMetadata(path, name);
}
- if( orig_name )
- free(orig_name);
- if( !detailID )
- {
- DPRINTF(E_WARN, L_SCANNER, "Unsuccessful getting details for %s!\n", path);
- return -1;
- }
-
- sprintf(objectID, "%s%s$%X", BROWSEDIR_ID, parentID, object);
-
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('%s', '%s%s', '%s', %"PRId64", '%q')",
- objectID, BROWSEDIR_ID, parentID, class, detailID, name);
-
- if( *parentID )
+ else if( is_video(name) )
{
- typedir_objectID = 0;
- typedir_parentID = strdup(parentID);
- baseid = strrchr(typedir_parentID, '$');
- if( baseid )
- {
- typedir_objectID = strtol(baseid+1, NULL, 16);
- *baseid = '\0';
- }
- insert_directory(name, path, base, typedir_parentID, typedir_objectID);
- free(typedir_parentID);
+ strcpy(base, VIDEO_DIR_ID);
+ strcpy(class, "item.videoItem");
+ detailID = GetVideoMetadata(path, name);
}
- sql_exec(db, "INSERT into OBJECTS"
- " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) "
- "VALUES"
- " ('%s%s$%X', '%s%s', '%s', '%s', %"PRId64", '%q')",
- base, parentID, object, base, parentID, objectID, class, detailID, name);
-
+ //DEBUG printf("Got DetailID %lu!\n", detailID);
+
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, PATH, NAME) "
+ "VALUES"
+ " ('%s', '%s%s', '%s', %lu, '%q', '%q')",
+ objectID, BROWSEDIR_ID, parentID, class, detailID, path, name);
+ //DEBUG printf("SQL: %s\n", sql);
+ sql_exec(db, sql);
+ sqlite3_free(sql);
+ #if 0
+ sql = sqlite3_mprintf( "INSERT into OBJECTS"
+ " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, PATH, NAME) "
+ "VALUES"
+ " ('%s%s$%X', '%s%s', '%s', '%s', %lu, '%q', '%q')",
+ base, parentID, object, base, parentID, objectID, class, detailID, path, name);
+ //DEBUG printf("SQL: %s\n", sql);
+ sql_exec(db, sql);
+ sqlite3_free(sql);
+ #else
insert_containers(name, path, objectID, class, detailID);
- return 0;
+ #endif
+ return -1;
}
int
-CreateDatabase(void)
+create_database(void)
{
int ret, i;
- const char * containers[] = { "0","-1", "root",
- MUSIC_ID, "0", _("Music"),
- MUSIC_ALL_ID, MUSIC_ID, _("All Music"),
- MUSIC_GENRE_ID, MUSIC_ID, _("Genre"),
- MUSIC_ARTIST_ID, MUSIC_ID, _("Artist"),
- MUSIC_ALBUM_ID, MUSIC_ID, _("Album"),
- MUSIC_DIR_ID, MUSIC_ID, _("Folders"),
- MUSIC_PLIST_ID, MUSIC_ID, _("Playlists"),
-
- VIDEO_ID, "0", _("Video"),
- VIDEO_ALL_ID, VIDEO_ID, _("All Video"),
- VIDEO_DIR_ID, VIDEO_ID, _("Folders"),
-
- IMAGE_ID, "0", _("Pictures"),
- IMAGE_ALL_ID, IMAGE_ID, _("All Pictures"),
- IMAGE_DATE_ID, IMAGE_ID, _("Date Taken"),
- IMAGE_CAMERA_ID, IMAGE_ID, _("Camera"),
- IMAGE_DIR_ID, IMAGE_ID, _("Folders"),
-
- BROWSEDIR_ID, "0", _("Browse Folders"),
- 0 };
+ char sql_buf[512];
+ const char * containers[] = { "0","-1", "root",
+ "1", "0", "Music",
+ "1$4", "1", "All Music",
+ "1$5", "1", "Genre",
+ "1$6", "1", "Artist",
+ "1$7", "1", "Album",
+ "1$20", "1", "Folders",
+ "2", "0", "Video",
+ "2$8", "2", "All Video",
+ "2$21", "2", "Folders",
+ "3", "0", "Pictures",
+ "3$11", "3", "All Pictures",
+ "3$12", "3", "Date Taken",
+ "3$13", "3", "Camera",
+ "3$22", "3", "Folders",
+ "64", "0", "Browse Folders",
+ 0 };
+
+ sql_exec(db, "pragma temp_store = MEMORY");
+ sql_exec(db, "pragma synchronous = OFF;");
+ sql_exec(db, "pragma cache_size = 8192;");
ret = sql_exec(db, "CREATE TABLE OBJECTS ( "
"ID INTEGER PRIMARY KEY AUTOINCREMENT, "
- "OBJECT_ID TEXT UNIQUE NOT NULL, "
+ "OBJECT_ID TEXT NOT NULL, "
"PARENT_ID TEXT NOT NULL, "
"REF_ID TEXT DEFAULT NULL, "
"CLASS TEXT NOT NULL, "
"DETAIL_ID INTEGER DEFAULT NULL, "
+ "PATH TEXT DEFAULT NULL, "
"NAME TEXT DEFAULT NULL"
");");
if( ret != SQLITE_OK )
goto sql_failed;
ret = sql_exec(db, "CREATE TABLE DETAILS ( "
"ID INTEGER PRIMARY KEY AUTOINCREMENT, "
- "PATH TEXT DEFAULT NULL, "
"SIZE INTEGER, "
- "TITLE TEXT COLLATE NOCASE, "
+ "TITLE TEXT, "
"DURATION TEXT, "
"BITRATE INTEGER, "
"SAMPLERATE INTEGER, "
- "ARTIST TEXT COLLATE NOCASE, "
- "ALBUM TEXT COLLATE NOCASE, "
- "GENRE TEXT COLLATE NOCASE, "
+ "ARTIST TEXT, "
+ "ALBUM TEXT, "
+ "GENRE TEXT, "
"COMMENT TEXT, "
"CHANNELS INTEGER, "
"TRACK INTEGER, "
"DATE DATE, "
- "RESOLUTION TEXT, "
+ "WIDTH TEXT, "
+ "HEIGHT TEXT, "
"THUMBNAIL BOOL DEFAULT 0, "
- "CREATOR TEXT COLLATE NOCASE, "
+ "CREATOR TEXT, "
"DLNA_PN TEXT, "
- "MIME TEXT, "
- "ALBUM_ART INTEGER DEFAULT 0, "
- "DISC INTEGER, "
- "TIMESTAMP INTEGER"
- ")");
- if( ret != SQLITE_OK )
- goto sql_failed;
- ret = sql_exec(db, "CREATE TABLE ALBUM_ART ( "
- "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
- "PATH TEXT NOT NULL"
- ")");
- if( ret != SQLITE_OK )
- goto sql_failed;
- ret = sql_exec(db, "CREATE TABLE CAPTIONS ("
- "ID INTEGER PRIMARY KEY, "
- "PATH TEXT NOT NULL"
- ")");
- if( ret != SQLITE_OK )
- goto sql_failed;
- ret = sql_exec(db, "CREATE TABLE PLAYLISTS ("
- "ID INTEGER PRIMARY KEY AUTOINCREMENT, "
- "NAME TEXT NOT NULL, "
- "PATH TEXT NOT NULL, "
- "ITEMS INTEGER DEFAULT 0, "
- "FOUND INTEGER DEFAULT 0"
- ")");
- if( ret != SQLITE_OK )
- goto sql_failed;
- ret = sql_exec(db, "CREATE TABLE SETTINGS ("
- "UPDATE_ID INTEGER PRIMARY KEY DEFAULT 0, "
- "FLAGS INTEGER DEFAULT 0"
- ")");
- if( ret != SQLITE_OK )
- goto sql_failed;
- ret = sql_exec(db, "INSERT into SETTINGS values (0, 0)");
+ "MIME TEXT"
+ ");");
if( ret != SQLITE_OK )
goto sql_failed;
for( i=0; containers[i]; i=i+3 )
{
- ret = sql_exec(db, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME)"
- " values "
- "('%s', '%s', %lld, 'container.storageFolder', '%q')",
- containers[i], containers[i+1], GetFolderMetadata(containers[i+2], NULL, NULL, NULL, NULL), containers[i+2]);
+ sprintf(sql_buf, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, CLASS, NAME) values ( '%s', '%s', 'container.storageFolder', '%s')",
+ containers[i], containers[i+1], containers[i+2]);
+ ret = sql_exec(db, sql_buf);
if( ret != SQLITE_OK )
goto sql_failed;
}
+ sql_exec(db, "create TEMP TABLE ARTISTS (ITEM TEXT, OBJECT_ID TEXT, SUBITEM TEXT DEFAULT NULL);");
+ sql_exec(db, "create TEMP TABLE ALBUMS (ITEM TEXT, OBJECT_ID TEXT, SUBITEM TEXT DEFAULT NULL);");
+ sql_exec(db, "create TEMP TABLE GENRES (ITEM TEXT, OBJECT_ID TEXT, SUBITEM TEXT DEFAULT NULL);");
+ sql_exec(db, "create TEMP TABLE DATES (ITEM TEXT, OBJECT_ID TEXT, SUBITEM TEXT DEFAULT NULL);");
+ sql_exec(db, "create TEMP TABLE CAMS (ITEM TEXT, OBJECT_ID TEXT, SUBITEM TEXT DEFAULT NULL);");
+ sql_exec(db, "create TEMP TABLE CAMDATE (ITEM TEXT, OBJECT_ID TEXT, SUBITEM TEXT DEFAULT NULL);");
+
sql_exec(db, "create INDEX IDX_OBJECTS_OBJECT_ID ON OBJECTS(OBJECT_ID);");
sql_exec(db, "create INDEX IDX_OBJECTS_PARENT_ID ON OBJECTS(PARENT_ID);");
sql_exec(db, "create INDEX IDX_OBJECTS_DETAIL_ID ON OBJECTS(DETAIL_ID);");
sql_exec(db, "create INDEX IDX_OBJECTS_CLASS ON OBJECTS(CLASS);");
- sql_exec(db, "create INDEX IDX_DETAILS_PATH ON DETAILS(PATH);");
+ sql_exec(db, "create INDEX IDX_OBJECTS_PATH ON OBJECTS(PATH);");
sql_exec(db, "create INDEX IDX_DETAILS_ID ON DETAILS(ID);");
- sql_exec(db, "create INDEX IDX_ALBUM_ART ON ALBUM_ART(ID);");
- sql_exec(db, "create INDEX IDX_SCANNER_OPT ON OBJECTS(PARENT_ID, NAME, OBJECT_ID);");
+
sql_failed:
if( ret != SQLITE_OK )
@@ -648,181 +440,71 @@ sql_failed:
}
int
-filter_audio(const struct dirent *d)
-{
- return ( (*d->d_name != '.') &&
- ((d->d_type == DT_DIR) ||
- (d->d_type == DT_LNK) ||
- (d->d_type == DT_UNKNOWN) ||
- ((d->d_type == DT_REG) &&
- (is_audio(d->d_name) ||
- is_playlist(d->d_name)
- )
- ) ));
-}
-
-int
-filter_video(const struct dirent *d)
-{
- return ( (*d->d_name != '.') &&
- ((d->d_type == DT_DIR) ||
- (d->d_type == DT_LNK) ||
- (d->d_type == DT_UNKNOWN) ||
- ((d->d_type == DT_REG) &&
- is_video(d->d_name) )
- ) );
-}
-
-int
-filter_images(const struct dirent *d)
-{
- return ( (*d->d_name != '.') &&
- ((d->d_type == DT_DIR) ||
- (d->d_type == DT_LNK) ||
- (d->d_type == DT_UNKNOWN) ||
- ((d->d_type == DT_REG) &&
- is_image(d->d_name) )
- ) );
-}
-
-int
filter_media(const struct dirent *d)
{
+ struct stat entry;
return ( (*d->d_name != '.') &&
- ((d->d_type == DT_DIR) ||
- (d->d_type == DT_LNK) ||
- (d->d_type == DT_UNKNOWN) ||
- ((d->d_type == DT_REG) &&
- (is_image(d->d_name) ||
- is_audio(d->d_name) ||
- is_video(d->d_name) ||
- is_playlist(d->d_name)
- )
- ) ));
+ (stat(d->d_name, &entry) == 0) &&
+ (S_ISDIR(entry.st_mode) ||
+ (S_ISREG(entry.st_mode) &&
+ (is_image(d->d_name) ||
+ is_audio(d->d_name) ||
+ is_video(d->d_name)
+ )
+ )) );
}
void
-ScanDirectory(const char * dir, const char * parent, enum media_types dir_type)
+ScanDirectory(const char * dir, const char * parent)
{
struct dirent **namelist;
- int i, n, startID=0;
+ struct stat entry;
+ int n, i;
char parent_id[PATH_MAX];
char full_path[PATH_MAX];
- char * name = NULL;
- static long long unsigned int fileno = 0;
- enum file_types type;
+ char * name;
+
+ if( !parent )
+ {
+ if( create_database() != 0 )
+ {
+ fprintf(stderr, "Error creating database!\n");
+ return;
+ }
+ }
setlocale(LC_COLLATE, "");
if( chdir(dir) != 0 )
return;
-
- DPRINTF(parent?E_INFO:E_WARN, L_SCANNER, _("Scanning %s\n"), dir);
- switch( dir_type )
- {
- case ALL_MEDIA:
- n = scandir(".", &namelist, filter_media, alphasort);
- break;
- case AUDIO_ONLY:
- n = scandir(".", &namelist, filter_audio, alphasort);
- break;
- case VIDEO_ONLY:
- n = scandir(".", &namelist, filter_video, alphasort);
- break;
- case IMAGES_ONLY:
- n = scandir(".", &namelist, filter_images, alphasort);
- break;
- default:
- n = -1;
- break;
- }
+ n = scandir(".", &namelist, filter_media, alphasort);
if (n < 0) {
fprintf(stderr, "Error scanning %s [scandir]\n", dir);
return;
}
-
- if( !parent )
- {
- startID = get_next_available_id("OBJECTS", BROWSEDIR_ID);
- }
-
- for (i=0; i < n; i++)
- {
-#if !USE_FORK
- if( quitting )
- break;
-#endif
- type = TYPE_UNKNOWN;
- sprintf(full_path, "%s/%s", dir, namelist[i]->d_name);
- name = escape_tag(namelist[i]->d_name, 1);
- if( namelist[i]->d_type == DT_DIR )
- {
- type = TYPE_DIR;
- }
- else if( namelist[i]->d_type == DT_REG )
- {
- type = TYPE_FILE;
- }
- else
- {
- type = resolve_unknown_type(full_path, dir_type);
- }
- if( (type == TYPE_DIR) && (access(full_path, R_OK|X_OK) == 0) )
- {
- insert_directory(name, full_path, BROWSEDIR_ID, (parent ? parent:""), i+startID);
- sprintf(parent_id, "%s$%X", (parent ? parent:""), i+startID);
- ScanDirectory(full_path, parent_id, dir_type);
- }
- else if( type == TYPE_FILE )
+ for (i=0; i < n; i++) {
+ if( stat(namelist[i]->d_name, &entry) == 0 )
{
- if( insert_file(name, full_path, (parent ? parent:""), i+startID) == 0 )
- fileno++;
+ name = NULL;
+ sprintf(full_path, "%s/%s", dir, namelist[i]->d_name);
+ if( index(namelist[i]->d_name, '&') )
+ {
+ name = modifyString(strdup(namelist[i]->d_name), "&", "&amp;amp;", 0);
+ }
+ if( S_ISDIR(entry.st_mode) )
+ {
+ insert_directory(name?name:namelist[i]->d_name, full_path, (parent ? parent:""), i);
+ sprintf(parent_id, "%s$%X", (parent ? parent:""), i);
+ ScanDirectory(full_path, parent_id);
+ }
+ else
+ {
+ insert_file(name?name:namelist[i]->d_name, full_path, (parent ? parent:""), i);
+ }
}
- free(name);
+ if( name )
+ free(name);
free(namelist[i]);
}
free(namelist);
- if( parent )
- {
- chdir(dirname((char*)dir));
- }
- else
- {
- DPRINTF(E_WARN, L_SCANNER, _("Scanning %s finished (%llu files)!\n"), dir, fileno);
- }
-}
-
-void
-start_scanner()
-{
- struct media_dir_s * media_path = media_dirs;
-
- if (setpriority(PRIO_PROCESS, 0, 15) == -1)
- DPRINTF(E_WARN, L_INOTIFY, "Failed to reduce scanner thread priority\n");
-
-#ifdef READYNAS
- FILE * flag = fopen("/ramfs/.upnp-av_scan", "w");
- if( flag )
- fclose(flag);
-#endif
- freopen("/dev/null", "a", stderr);
- while( media_path )
- {
- ScanDirectory(media_path->path, NULL, media_path->type);
- media_path = media_path->next;
- }
- freopen("/proc/self/fd/2", "a", stderr);
-#ifdef READYNAS
- if( access("/ramfs/.rescan_done", F_OK) == 0 )
- system("/bin/sh /ramfs/.rescan_done");
- unlink("/ramfs/.upnp-av_scan");
-#endif
- /* Create this index after scanning, so it doesn't slow down the scanning process.
- * This index is very useful for large libraries used with an XBox360 (or any
- * client that uses UPnPSearch on large containers). */
- sql_exec(db, "create INDEX IDX_SEARCH_OPT ON OBJECTS(OBJECT_ID, CLASS, DETAIL_ID);");
-
- fill_playlists();
-
- //JM: Set up a db version number, so we know if we need to rebuild due to a new structure.
- sql_exec(db, "pragma user_version = %d;", DB_VERSION);
+ chdir("..");
}
diff --git a/scanner.h b/scanner.h
index a57eedd..0e4747c 100644
--- a/scanner.h
+++ b/scanner.h
@@ -3,84 +3,19 @@
* Project : minidlna
* Website : http://sourceforge.net/projects/minidlna/
* Author : Justin Maggard
- *
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- */
+ * Copyright (c) 2008 Justin Maggard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
#ifndef __SCANNER_H__
#define __SCANNER_H__
-/* Try to be generally PlaysForSure compatible by using similar IDs */
-#define BROWSEDIR_ID "64"
-
-#define MUSIC_ID "1"
-#define MUSIC_ALL_ID "1$4"
-#define MUSIC_GENRE_ID "1$5"
-#define MUSIC_ARTIST_ID "1$6"
-#define MUSIC_ALBUM_ID "1$7"
-#define MUSIC_PLIST_ID "1$F"
-#define MUSIC_DIR_ID "1$14"
-#define MUSIC_CONTRIB_ARTIST_ID "1$100"
-#define MUSIC_ALBUM_ARTIST_ID "1$107"
-#define MUSIC_COMPOSER_ID "1$108"
-#define MUSIC_RATING_ID "1$101"
-
-#define VIDEO_ID "2"
-#define VIDEO_ALL_ID "2$8"
-#define VIDEO_GENRE_ID "2$9"
-#define VIDEO_ACTOR_ID "2$A"
-#define VIDEO_SERIES_ID "2$E"
-#define VIDEO_PLIST_ID "2$10"
-#define VIDEO_DIR_ID "2$15"
-#define VIDEO_RATING_ID "2$200"
-
-#define IMAGE_ID "3"
-#define IMAGE_ALL_ID "3$B"
-#define IMAGE_DATE_ID "3$C"
-#define IMAGE_ALBUM_ID "3$D"
-#define IMAGE_CAMERA_ID "3$D2" // PlaysForSure == Keyword
-#define IMAGE_PLIST_ID "3$11"
-#define IMAGE_DIR_ID "3$16"
-#define IMAGE_RATING_ID "3$300"
-
-extern int valid_cache;
-
-int
-is_video(const char * file);
-
-int
-is_audio(const char * file);
-
-int
-is_image(const char * file);
-
-sqlite_int64
-get_next_available_id(const char * table, const char * parentID);
-
-int
-insert_directory(const char * name, const char * path, const char * base, const char * parentID, int objectID);
-
-int
-insert_file(char * name, const char * path, const char * parentID, int object);
-
-int
-CreateDatabase(void);
+#define BROWSEDIR_ID "64"
+#define MUSIC_DIR_ID "1$20"
+#define VIDEO_DIR_ID "2$21"
+#define IMAGE_DIR_ID "3$22"
void
-start_scanner();
+ScanDirectory(const char * dir, const char * parent);
#endif
diff --git a/sql.c b/sql.c
index 6311e1e..9883987 100644
--- a/sql.c
+++ b/sql.c
@@ -1,200 +1,53 @@
-/* MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
+/* MiniDLNA media server
+ * Copyright (C) 2008 Justin Maggard
*
- * This file is part of MiniDLNA.
+ * 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.
*
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * 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.
*
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
#include "sql.h"
-#include "log.h"
int
-sql_exec(sqlite3 *db, const char *fmt, ...)
+sql_exec(sqlite3 * db, const char * sql)
{
int ret;
char *errMsg = NULL;
- char *sql;
- va_list ap;
- //DPRINTF(E_DEBUG, L_DB_SQL, "SQL: %s\n", sql);
-
- va_start(ap, fmt);
+ //DEBUG printf("SQL: %s\n", sql);
- sql = sqlite3_vmprintf(fmt, ap);
+ //ret = sqlite3_exec(db, sql, 0, 0, &errMsg);
ret = sqlite3_exec(db, sql, 0, 0, &errMsg);
if( ret != SQLITE_OK )
{
- DPRINTF(E_ERROR, L_DB_SQL, "SQL ERROR %d [%s]\n%s\n", ret, errMsg, sql);
+ fprintf(stderr, "SQL ERROR %d [%s]\n%s\n", ret, errMsg, sql);
if (errMsg)
sqlite3_free(errMsg);
}
- sqlite3_free(sql);
-
return ret;
}
int
-sql_get_table(sqlite3 *db, const char *sql, char ***pazResult, int *pnRow, int *pnColumn)
+sql_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int *pnColumn, char **pzErrmsg)
{
+ //DEBUG printf("SQL: %s\n", zSql);
int ret;
- char *errMsg = NULL;
- //DPRINTF(E_DEBUG, L_DB_SQL, "SQL: %s\n", sql);
-
- ret = sqlite3_get_table(db, sql, pazResult, pnRow, pnColumn, &errMsg);
+ ret = sqlite3_get_table(db, zSql, pazResult, pnRow, pnColumn, pzErrmsg);
if( ret != SQLITE_OK )
{
- DPRINTF(E_ERROR, L_DB_SQL, "SQL ERROR %d [%s]\n%s\n", ret, errMsg, sql);
- if (errMsg)
- sqlite3_free(errMsg);
+ fprintf(stderr, "SQL ERROR [%s]\n%s\n", *pzErrmsg, zSql);
+ if (*pzErrmsg)
+ sqlite3_free(*pzErrmsg);
}
-
- return ret;
-}
-
-int
-sql_get_int_field(sqlite3 *db, const char *fmt, ...)
-{
- va_list ap;
- int counter, result;
- char *sql;
- int ret;
- sqlite3_stmt *stmt;
-
- va_start(ap, fmt);
-
- sql = sqlite3_vmprintf(fmt, ap);
-
- //DPRINTF(E_DEBUG, L_DB_SQL, "sql: %s\n", sql);
-
- switch (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL))
- {
- case SQLITE_OK:
- break;
- default:
- DPRINTF(E_ERROR, L_DB_SQL, "prepare failed: %s\n%s\n", sqlite3_errmsg(db), sql);
- sqlite3_free(sql);
- return -1;
- }
- sqlite3_free(sql);
-
- for (counter = 0;
- ((result = sqlite3_step(stmt)) == SQLITE_BUSY || result == SQLITE_LOCKED) && counter < 2;
- counter++) {
- /* While SQLITE_BUSY has a built in timeout,
- SQLITE_LOCKED does not, so sleep */
- if (result == SQLITE_LOCKED)
- sleep(1);
- }
-
- switch (result)
- {
- case SQLITE_DONE:
- /* no rows returned */
- ret = 0;
- break;
- case SQLITE_ROW:
- if (sqlite3_column_type(stmt, 0) == SQLITE_NULL)
- {
- ret = 0;
- break;
- }
- ret = sqlite3_column_int(stmt, 0);
- break;
- default:
- DPRINTF(E_WARN, L_DB_SQL, "%s: step failed: %s\n", __func__, sqlite3_errmsg(db));
- ret = -1;
- break;
- }
-
- sqlite3_finalize(stmt);
return ret;
}
-char *
-sql_get_text_field(void *db, const char *fmt, ...)
-{
- va_list ap;
- int counter, result, len;
- char *sql;
- char *str;
- sqlite3_stmt *stmt;
-
- va_start(ap, fmt);
-
- if (db == NULL)
- {
- DPRINTF(E_WARN, L_DB_SQL, "db is NULL\n");
- return NULL;
- }
-
- sql = sqlite3_vmprintf(fmt, ap);
-
- //DPRINTF(E_DEBUG, L_DB_SQL, "sql: %s\n", sql);
-
- switch (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL))
- {
- case SQLITE_OK:
- break;
- default:
- DPRINTF(E_ERROR, L_DB_SQL, "prepare failed: %s\n%s\n", sqlite3_errmsg(db), sql);
- sqlite3_free(sql);
- return NULL;
- }
- sqlite3_free(sql);
-
- for (counter = 0;
- ((result = sqlite3_step(stmt)) == SQLITE_BUSY || result == SQLITE_LOCKED) && counter < 2;
- counter++)
- {
- /* While SQLITE_BUSY has a built in timeout,
- * SQLITE_LOCKED does not, so sleep */
- if (result == SQLITE_LOCKED)
- sleep(1);
- }
-
- switch (result)
- {
- case SQLITE_DONE:
- /* no rows returned */
- str = NULL;
- break;
-
- case SQLITE_ROW:
- if (sqlite3_column_type(stmt, 0) == SQLITE_NULL)
- {
- str = NULL;
- break;
- }
-
- len = sqlite3_column_bytes(stmt, 0);
- if ((str = sqlite3_malloc(len + 1)) == NULL)
- {
- DPRINTF(E_ERROR, L_DB_SQL, "malloc failed\n");
- break;
- }
-
- strncpy(str, (char *)sqlite3_column_text(stmt, 0), len + 1);
- break;
-
- default:
- DPRINTF(E_WARN, L_DB_SQL, "SQL step failed: %s\n", sqlite3_errmsg(db));
- str = NULL;
- break;
- }
-
- sqlite3_finalize(stmt);
- return str;
-}
diff --git a/sql.h b/sql.h
index e2e9661..7e15511 100644
--- a/sql.h
+++ b/sql.h
@@ -3,39 +3,19 @@
* Project : minidlna
* Website : http://sourceforge.net/projects/minidlna/
* Author : Justin Maggard
- *
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- */
+ * Copyright (c) 2008 Justin Maggard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file provided in this distribution.
+ * */
#ifndef __SQL_H__
#define __SQL_H__
#include <sqlite3.h>
int
-sql_exec(sqlite3 *db, const char *fmt, ...);
-
-int
-sql_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int *pnColumn);
+sql_exec(sqlite3 * db, const char * sql);
int
-sql_get_int_field(sqlite3 *db, const char *fmt, ...);
-
-char *
-sql_get_text_field(void *dbh, const char *fmt, ...);
+sql_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int *pnColumn, char **pzErrmsg);
#endif
diff --git a/testupnpdescgen.c b/testupnpdescgen.c
index 398d731..d072d7a 100644
--- a/testupnpdescgen.c
+++ b/testupnpdescgen.c
@@ -1,32 +1,10 @@
/* $Id$ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006-2008, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006-2008 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -35,15 +13,12 @@
#include "upnpdescgen.h"
char uuidvalue[] = "uuid:12345678-0000-0000-0000-00000000abcd";
-char friendly_name[] = "localhost: system_type";
char serialnumber[] = "12345678";
-char modelname[] = "MiniDLNA";
char modelnumber[] = "1";
char presentationurl[] = "http://192.168.0.1:8080/";
-unsigned int updateID = 0;
-#if PNPX
-char pnpx_hwid[] = "VEN_01F2&amp;DEV_0101&amp;REV_01 VEN_0033&amp;DEV_0001&amp;REV_01";
-#endif
+
+char * use_ext_ip_addr = NULL;
+const char * ext_if_name = "eth0";
int getifaddr(const char * ifname, char * buf, int len)
{
@@ -127,25 +102,17 @@ main(int argc, char * * argv)
rootDesc = genRootDesc(&rootDescLen);
xml_pretty_print(rootDesc, rootDescLen, stdout);
free(rootDesc);
- printf("\n----------------\n");
- printf("ContentDirectory\n");
- printf("----------------\n");
+ printf("\n-------------\n");
s = genContentDirectory(&l);
xml_pretty_print(s, l, stdout);
free(s);
- printf("\n----------------\n");
- printf("ConnectionManager\n");
- printf("----------------\n");
+ printf("\n-------------\n");
s = genConnectionManager(&l);
xml_pretty_print(s, l, stdout);
free(s);
- printf("\n----------------\n");
- printf("X_MS_MRR\n");
- printf("----------------\n");
- s = genX_MS_MediaReceiverRegistrar(&l);
- xml_pretty_print(s, l, stdout);
- free(s);
printf("\n-------------\n");
+#ifdef ENABLE_EVENTS
+#endif
/*
stupid_test();
*/
diff --git a/upnpdescgen.c b/upnpdescgen.c
index 97eaabd..b937b78 100644
--- a/upnpdescgen.c
+++ b/upnpdescgen.c
@@ -1,45 +1,25 @@
-/* $Id: upnpdescgen.c,v 1.18 2011/05/02 23:50:52 jmaggard Exp $ */
+/* $Id$ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006-2008, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006-2008 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
+
#include "config.h"
+#ifdef ENABLE_EVENTS
#include "getifaddr.h"
+#endif
#include "upnpdescgen.h"
-#include "minidlnapath.h"
+#include "miniupnpdpath.h"
#include "upnpglobalvars.h"
#include "upnpdescstrings.h"
-#undef DESC_DEBUG
-
static const char * const upnptypes[] =
{
"string",
@@ -104,11 +84,29 @@ static const char * const upnpallowedvalues[] =
"IN_PROGRESS",
"STOPPED",
0,
- RESOURCE_PROTOCOL_INFO_VALUES, /* 44 */
- 0,
- "0", /* 46 */
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," /* 44 */
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01,"
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED,"
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01,"
+ "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC,"
+ "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01,"
+ "http-get:*:audio/x-ms-wma:*,"
+ "http-get:*:audio/wav:*,"
+ "http-get:*:audio/mp4:*,"
+ "http-get:*:audio/x-aiff:*,"
+ "http-get:*:audio/x-flac:*,"
+ "http-get:*:application/ogg:*,"
+ "http-get:*:image/jpeg:*,"
+ "http-get:*:image/gif:*,"
+ "http-get:*:audio/x-mpegurl:*,"
+ "http-get:*:video/mpeg:*,"
+ "http-get:*:video/x-msvideo:*,"
+ "http-get:*:video/avi:*,"
+ "http-get:*:video/mpeg2:*,"
+ "http-get:*:video/dvd:*,"
+ "http-get:*:video/x-ms-wmv:*",
0,
- "", /* 48 */
+ "", /* 46 */
0
};
@@ -117,69 +115,35 @@ static const char xmlver[] =
static const char root_service[] =
"scpd xmlns=\"urn:schemas-upnp-org:service-1-0\"";
static const char root_device[] =
- "root xmlns=\"urn:schemas-upnp-org:device-1-0\""
-#if PNPX
- " xmlns:pnpx=\"http://schemas.microsoft.com/windows/pnpx/2005/11\""
- " xmlns:df=\"http://schemas.microsoft.com/windows/2008/09/devicefoundation\""
-#endif
- ;
+ "root xmlns=\"urn:schemas-upnp-org:device-1-0\"";
-/* root Description of the UPnP Device */
+/* root Description of the UPnP Device
+ * fixed to match UPnP_IGD_InternetGatewayDevice 1.0.pdf
+ * presentationURL is only "recommended" but the router doesn't appears
+ * in "Network connections" in Windows XP if it is not present. */
static const struct XMLElt rootDesc[] =
{
{root_device, INITHELPER(1,2)},
{"specVersion", INITHELPER(3,2)},
- {"device", INITHELPER(5,(14+PNPX))},
+ {"device", INITHELPER(5,13)},
{"/major", "1"},
{"/minor", "0"},
{"/deviceType", "urn:schemas-upnp-org:device:MediaServer:1"},
-#if PNPX == 5
- {"/pnpx:X_hardwareId", pnpx_hwid},
- {"/pnpx:X_compatibleId", "MS_DigitalMediaDeviceClass_DMS_V001"},
- {"/pnpx:X_deviceCategory", "MediaDevices"},
- {"/df:X_deviceCategory", "Multimedia.DMS"},
- {"/microsoft:magicPacketWakeSupported xmlns:microsoft=\"urn:schemas-microsoft-com:WMPNSS-1-0\"", "0"},
-#endif
- {"/friendlyName", friendly_name}, /* required */
+ {"/friendlyName", ROOTDEV_FRIENDLYNAME}, /* required */
{"/manufacturer", ROOTDEV_MANUFACTURER}, /* required */
{"/manufacturerURL", ROOTDEV_MANUFACTURERURL}, /* optional */
{"/modelDescription", ROOTDEV_MODELDESCRIPTION}, /* recommended */
- {"/modelName", modelname}, /* required */
+ {"/modelName", ROOTDEV_MODELNAME}, /* required */
{"/modelNumber", modelnumber},
{"/modelURL", ROOTDEV_MODELURL},
{"/serialNumber", serialnumber},
{"/UDN", uuidvalue}, /* required */
{"/dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"", "DMS-1.50"},
{"/presentationURL", presentationurl}, /* recommended */
- {"iconList", INITHELPER((19+PNPX),4)},
- {"serviceList", INITHELPER((43+PNPX),3)},
- {"icon", INITHELPER((23+PNPX),5)},
- {"icon", INITHELPER((28+PNPX),5)},
- {"icon", INITHELPER((33+PNPX),5)},
- {"icon", INITHELPER((38+PNPX),5)},
- {"/mimetype", "image/png"},
- {"/width", "48"},
- {"/height", "48"},
- {"/depth", "24"},
- {"/url", "/icons/sm.png"},
- {"/mimetype", "image/png"},
- {"/width", "120"},
- {"/height", "120"},
- {"/depth", "24"},
- {"/url", "/icons/lrg.png"},
- {"/mimetype", "image/jpeg"},
- {"/width", "48"},
- {"/height", "48"},
- {"/depth", "24"},
- {"/url", "/icons/sm.jpg"},
- {"/mimetype", "image/jpeg"},
- {"/width", "120"},
- {"/height", "120"},
- {"/depth", "24"},
- {"/url", "/icons/lrg.jpg"},
- {"service", INITHELPER((46+PNPX),5)},
- {"service", INITHELPER((51+PNPX),5)},
- {"service", INITHELPER((56+PNPX),5)},
+ {"serviceList", INITHELPER(18,3)},
+ {"service", INITHELPER(21,5)},
+ {"service", INITHELPER(26,5)},
+ {"service", INITHELPER(31,5)},
{"/serviceType", "urn:schemas-upnp-org:service:ContentDirectory:1"},
{"/serviceId", "urn:upnp-org:serviceId:ContentDirectory"},
{"/controlURL", CONTENTDIRECTORY_CONTROLURL},
@@ -211,6 +175,12 @@ static const struct argument AddPortMappingArgs[] =
{NULL, 0, 0}
};
+static const struct argument GetExternalIPAddressArgs[] =
+{
+ {NULL, 2, 7},
+ {NULL, 0, 0}
+};
+
static const struct argument DeletePortMappingArgs[] =
{
{NULL, 1, 11},
@@ -232,6 +202,14 @@ static const struct argument GetConnectionTypeInfoArgs[] =
{NULL, 0, 0}
};
+static const struct argument GetStatusInfoArgs[] =
+{
+ {NULL, 2, 2},
+ {NULL, 2, 4},
+ {NULL, 2, 3},
+ {NULL, 0, 0}
+};
+
static const struct argument GetNATRSIPStatusArgs[] =
{
{NULL, 2, 5},
@@ -323,9 +301,9 @@ static const struct action ConnectionManagerActions[] =
static const struct stateVar ConnectionManagerVars[] =
{
- {"SourceProtocolInfo", 1<<7, 0, 0, 44}, /* required */
- {"SinkProtocolInfo", 1<<7, 0, 0, 48}, /* required */
- {"CurrentConnectionIDs", 1<<7, 0, 0, 46}, /* required */
+ {"SourceProtocolInfo", 1<<7, 0, 44, 44}, /* required */
+ {"SinkProtocolInfo", 1<<7, 0}, /* required */
+ {"CurrentConnectionIDs", 1<<7, 0}, /* required */
{"A_ARG_TYPE_ConnectionStatus", 0, 0, 27}, /* required */
{"A_ARG_TYPE_ConnectionManager", 0, 0}, /* required */
{"A_ARG_TYPE_Direction", 0, 0, 33}, /* required */
@@ -338,19 +316,19 @@ static const struct stateVar ConnectionManagerVars[] =
static const struct argument GetSearchCapabilitiesArgs[] =
{
- {"SearchCaps", 2, 10},
+ {"SearchCaps", 2, 16},
{0, 0}
};
static const struct argument GetSortCapabilitiesArgs[] =
{
- {"SortCaps", 2, 11},
+ {"SortCaps", 2, 17},
{0, 0}
};
static const struct argument GetSystemUpdateIDArgs[] =
{
- {"Id", 2, 12},
+ {"Id", 2, 18},
{0, 0}
};
@@ -407,7 +385,7 @@ static const struct action ContentDirectoryActions[] =
static const struct stateVar ContentDirectoryVars[] =
{
- {"TransferIDs", 1<<7, 0, 0, 48}, /* 0 */
+ {"TransferIDs", 1<<7, 0, 46, 46}, /* 0 */
{"A_ARG_TYPE_ObjectID", 0, 0},
{"A_ARG_TYPE_Result", 0, 0},
{"A_ARG_TYPE_SearchCriteria", 0, 0},
@@ -418,6 +396,12 @@ static const struct stateVar ContentDirectoryVars[] =
{"A_ARG_TYPE_Index", 3, 0},
{"A_ARG_TYPE_Count", 3, 0},
{"A_ARG_TYPE_UpdateID", 3, 0},
+ {"A_ARG_TYPE_UpdateID", 3, 0},
+ {"A_ARG_TYPE_UpdateID", 3, 0},
+ {"A_ARG_TYPE_UpdateID", 3, 0},
+ {"A_ARG_TYPE_UpdateID", 3, 0},
+ {"A_ARG_TYPE_UpdateID", 3, 0},
+ {"A_ARG_TYPE_UpdateID", 3, 0},
//JM{"A_ARG_TYPE_TransferID", 3, 0}, /* 10 */
//JM{"A_ARG_TYPE_TransferStatus", 0, 0, 39},
/* Allowed Values : COMPLETED / ERROR / IN_PROGRESS / STOPPED */
@@ -427,7 +411,7 @@ static const struct stateVar ContentDirectoryVars[] =
//JM{"A_ARG_TYPE_URI", 5, 0}, /* 15 */
{"SearchCapabilities", 0, 0},
{"SortCapabilities", 0, 0},
- {"SystemUpdateID", 3|0x80, 0, 0, 255},
+ {"SystemUpdateID", 3|0x80, 0, 46, 46},
//{"ContainerUpdateIDs", 0, 0},
{0, 0}
};
@@ -457,9 +441,7 @@ static const struct action X_MS_MediaReceiverRegistrarActions[] =
{
{"IsAuthorized", GetIsAuthorizedArgs}, /* R */
{"IsValidated", GetIsValidatedArgs}, /* R */
-#if 0 // Not needed? WMP12 still works. Need to check with 360 and WMP11.
{"RegisterDevice", GetRegisterDeviceArgs}, /* R */
-#endif
{0, 0}
};
@@ -469,10 +451,10 @@ static const struct stateVar X_MS_MediaReceiverRegistrarVars[] =
{"A_ARG_TYPE_RegistrationReqMsg", 7, 0},
{"A_ARG_TYPE_RegistrationRespMsg", 7, 0},
{"A_ARG_TYPE_Result", 6, 0},
- {"AuthorizationDeniedUpdateID", 3, 0},
- {"AuthorizationGrantedUpdateID", 3, 0},
- {"ValidationRevokedUpdateID", 3, 0},
- {"ValidationSucceededUpdateID", 3, 0},
+ {"AuthorizationDeniedUpdateID", (1<<7)|3, 0},
+ {"AuthorizationGrantedUpdateID", (1<<7)|3, 0},
+ {"ValidationRevokedUpdateID", (1<<7)|3, 0},
+ {"ValidationSucceededUpdateID", (1<<7)|3, 0},
{0, 0}
};
@@ -512,8 +494,74 @@ static const struct argument GetTotalPacketsReceivedArgs[] =
{NULL, 0, 0}
};
+static const struct action WANCfgActions[] =
+{
+ {"GetCommonLinkProperties", GetCommonLinkPropertiesArgs}, /* Required */
+ {"GetTotalBytesSent", GetTotalBytesSentArgs}, /* optional */
+ {"GetTotalBytesReceived", GetTotalBytesReceivedArgs}, /* optional */
+ {"GetTotalPacketsSent", GetTotalPacketsSentArgs}, /* optional */
+ {"GetTotalPacketsReceived", GetTotalPacketsReceivedArgs}, /* optional */
+ {0, 0}
+};
+
+/* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */
+static const struct stateVar WANCfgVars[] =
+{
+ {"WANAccessType", 0, 0, 1},
+ /* Allowed Values : DSL / POTS / Cable / Ethernet
+ * Default value : empty string */
+ {"Layer1UpstreamMaxBitRate", 3, 0},
+ {"Layer1DownstreamMaxBitRate", 3, 0},
+ {"PhysicalLinkStatus", 0|0x80, 0, 6, 6},
+ /* allowed values :
+ * Up / Down / Initializing (optional) / Unavailable (optionnal)
+ * no Default value
+ * Evented */
+ {"TotalBytesSent", 3, 0}, /* Optional */
+ {"TotalBytesReceived", 3, 0}, /* Optional */
+ {"TotalPacketsSent", 3, 0}, /* Optional */
+ {"TotalPacketsReceived", 3, 0},/* Optional */
+ /*{"MaximumActiveConnections", 2, 0}, // allowed Range value // OPTIONAL */
+ {0, 0}
+};
+
+static const struct serviceDesc scpdWANCfg =
+{ WANCfgActions, WANCfgVars };
+
+#ifdef ENABLE_L3F_SERVICE
+/* Read UPnP_IGD_Layer3Forwarding_1.0.pdf */
+static const struct argument SetDefaultConnectionServiceArgs[] =
+{
+ {NULL, 1, 0}, /* in */
+ {NULL, 0, 0}
+};
+
+static const struct argument GetDefaultConnectionServiceArgs[] =
+{
+ {NULL, 2, 0}, /* out */
+ {0, 0}
+};
+
+static const struct action L3FActions[] =
+{
+ {"SetDefaultConnectionService", SetDefaultConnectionServiceArgs}, /* Req */
+ {"GetDefaultConnectionService", GetDefaultConnectionServiceArgs}, /* Req */
+ {0, 0}
+};
+
+static const struct stateVar L3FVars[] =
+{
+ {"DefaultConnectionService", 0|0x80, 0, 0, 255}, /* Required */
+ {0, 0}
+};
+
+static const struct serviceDesc scpdL3F =
+{ L3FActions, L3FVars };
+#endif
+
static const struct serviceDesc scpdContentDirectory =
{ ContentDirectoryActions, ContentDirectoryVars };
+//{ ContentDirectoryActions, ContentDirectoryVars };
static const struct serviceDesc scpdConnectionManager =
{ ConnectionManagerActions, ConnectionManagerVars };
@@ -527,7 +575,6 @@ static const struct serviceDesc scpdX_MS_MediaReceiverRegistrar =
static char *
strcat_str(char * str, int * len, int * tmplen, const char * s2)
{
- char *p;
int s2len;
s2len = (int)strlen(s2);
if(*tmplen <= (*len + s2len))
@@ -536,17 +583,7 @@ strcat_str(char * str, int * len, int * tmplen, const char * s2)
*tmplen += 256;
else
*tmplen += s2len + 1;
- p = realloc(str, *tmplen);
- if (!p)
- {
- if(s2len < 256)
- *tmplen -= 256;
- else
- *tmplen -= s2len + 1;
- return str;
- }
- else
- str = p;
+ str = (char *)realloc(str, *tmplen);
}
/*strcpy(str + *len, s2); */
memcpy(str + *len, s2, s2len + 1);
@@ -560,18 +597,10 @@ strcat_str(char * str, int * len, int * tmplen, const char * s2)
static char *
strcat_char(char * str, int * len, int * tmplen, char c)
{
- char *p;
if(*tmplen <= (*len + 1))
{
*tmplen += 256;
- p = (char *)realloc(str, *tmplen);
- if (!p)
- {
- *tmplen -= 256;
- return str;
- }
- else
- str = p;
+ str = (char *)realloc(str, *tmplen);
}
str[*len] = c;
(*len)++;
@@ -584,7 +613,7 @@ static char *
genXML(char * str, int * len, int * tmplen,
const struct XMLElt * p)
{
- u_int16_t i, j, k;
+ unsigned short i, j, k;
int top;
const char * eltname, *s;
char c;
@@ -604,9 +633,7 @@ genXML(char * str, int * len, int * tmplen,
return str;
if(eltname[0] == '/')
{
- #ifdef DESC_DEBUG
- printf("DBG: <%s>%s<%s>\n", eltname+1, p[i].data, eltname);
- #endif
+ /*printf("<%s>%s<%s>\n", eltname+1, p[i].data, eltname); */
str = strcat_char(str, len, tmplen, '<');
str = strcat_str(str, len, tmplen, eltname+1);
str = strcat_char(str, len, tmplen, '>');
@@ -621,14 +648,10 @@ genXML(char * str, int * len, int * tmplen,
return str;
i = ++(pile[top].i);
j = pile[top].j;
- #ifdef DESC_DEBUG
- printf("DBG: pile[%d]\t%d %d\n", top, i, j);
- #endif
+ /*printf(" pile[%d]\t%d %d\n", top, i, j); */
if(i==j)
{
- #ifdef DESC_DEBUG
- printf("DBG: i==j, </%s>\n", pile[top].eltname);
- #endif
+ /*printf("</%s>\n", pile[top].eltname); */
str = strcat_char(str, len, tmplen, '<');
str = strcat_char(str, len, tmplen, '/');
s = pile[top].eltname;
@@ -643,21 +666,17 @@ genXML(char * str, int * len, int * tmplen,
}
else
{
- #ifdef DESC_DEBUG
- printf("DBG: [%d] <%s>\n", i, eltname);
- #endif
+ /*printf("<%s>\n", eltname); */
str = strcat_char(str, len, tmplen, '<');
str = strcat_str(str, len, tmplen, eltname);
str = strcat_char(str, len, tmplen, '>');
k = i;
/*i = p[k].index; */
/*j = i + p[k].nchild; */
- i = (unsigned long)p[k].data & 0xffff;
- j = i + ((unsigned long)p[k].data >> 16);
+ i = (unsigned)p[k].data & 0xffff;
+ j = i + ((unsigned)p[k].data >> 16);
top++;
- #ifdef DESC_DEBUG
- printf("DBG: +pile[%d]\t%d %d\n", top, i, j);
- #endif
+ /*printf(" +pile[%d]\t%d %d\n", top, i, j); */
pile[top].i = i;
pile[top].j = j;
pile[top].eltname = eltname;
@@ -679,12 +698,23 @@ genRootDesc(int * len)
str = (char *)malloc(tmplen);
if(str == NULL)
return NULL;
+#if 1
* len = strlen(xmlver);
/*strcpy(str, xmlver); */
memcpy(str, xmlver, *len + 1);
str = genXML(str, len, &tmplen, rootDesc);
str[*len] = '\0';
return str;
+#else
+char *ret = calloc(1, 8192);
+sprintf(ret, "<?xml version='1.0' encoding='UTF-8' ?>\r\n"
+"<root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType><friendlyName>MiniDLNA (MaggardMachine2)</friendlyName><manufacturer>NETGEAR</manufacturer><manufacturerURL>http://www.netgear.com</manufacturerURL><modelDescription>NETGEAR ReadyNAS NV</modelDescription><modelName>ReadyNAS</modelName><modelNumber>NV</modelNumber><modelURL>http://www.netgear.com</modelURL><UDN>uuid:aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d</UDN><dlna:X_DLNADOC>DMS-1.50</dlna:X_DLNADOC>\r\n"
+//"<root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"><specVersion><major>1</major><minor>0</minor></specVersion><device><deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType><friendlyName>MiniDLNA (MaggardMachine2)</friendlyName><manufacturer>NETGEAR</manufacturer><manufacturerURL>http://www.netgear.com</manufacturerURL><modelDescription>NETGEAR ReadyNAS NV</modelDescription><modelName>ReadyNAS</modelName><modelNumber>NV</modelNumber><modelURL>http://www.netgear.com</modelURL><UDN>uuid:aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d</UDN><dlna:X_DLNACAP>av-upload,image-upload,create-child-container,audio-upload</dlna:X_DLNACAP><dlna:X_DLNADOC>DMS-1.50</dlna:X_DLNADOC>\r\n"
+"<serviceList><service><serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType><serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId><SCPDURL>/ConnectionMgr.xml</SCPDURL><controlURL>/ctl/ConnectionMgr</controlURL><eventSubURL>/evt/ConnectionMgr</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType><serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId><SCPDURL>/ContentDir.xml</SCPDURL><controlURL>/ctl/ContentDir</controlURL><eventSubURL>/evt/ContentDir</eventSubURL></service></serviceList></device></root>");
+//"</iconList><serviceList><service><serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType><serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId><SCPDURL>/ConnectionMgr.xml</SCPDURL><controlURL>/ctl/ConnectionMgr</controlURL><eventSubURL>/evt/ConnectionMgr</eventSubURL></service><service><serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType><serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId><SCPDURL>/ContentDir.xml</SCPDURL><controlURL>/ctl/ContentDir</controlURL><eventSubURL>/evt/ContentDir</eventSubURL></service><service><serviceType>urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1</serviceType><serviceId>763f907c-8cfb-11dd-a382-c9c0ad9eae41</serviceId><SCPDURL>/upnp/aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d/aefdc437-8cf7-11dd-b3bb-ff0d6f9a7e6d</SCPDURL><controlURL>/upnp/aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d/control/aefdc437-8cf7-11dd-b3bb-ff0d6f9a7e6d</controlURL><eventSubURL>/upnp/aefc3d94-8cf7-11dd-b3bb-ff0d6f9a7e6d/events/aefdc437-8cf7-11dd-b3bb-ff0d6f9a7e6d</eventSubURL></service></serviceList></device></root>\r\n");
+ * len = strlen(ret);
+return ret;
+#endif
}
/* genServiceDesc() :
@@ -733,9 +763,18 @@ genServiceDesc(int * len, const struct serviceDesc * s)
j = 0;
while(args[j].dir)
{
+ //JM str = strcat_str(str, len, &tmplen, "<argument><name>New");
str = strcat_str(str, len, &tmplen, "<argument><name>");
p = vars[args[j].relatedVar].name;
- str = strcat_str(str, len, &tmplen, (args[j].name ? args[j].name : p));
+ if(0 == memcmp(p, "PortMapping", 11)
+ && 0 != memcmp(p + 11, "Description", 11)) {
+ if(0 == memcmp(p + 11, "NumberOfEntries", 15))
+ str = strcat_str(str, len, &tmplen, "PortMappingIndex");
+ else
+ str = strcat_str(str, len, &tmplen, p + 11);
+ } else {
+ str = strcat_str(str, len, &tmplen, (args[j].name ? args[j].name : p));
+ }
str = strcat_str(str, len, &tmplen, "</name><direction>");
str = strcat_str(str, len, &tmplen, args[j].dir==1?"in":"out");
str = strcat_str(str, len, &tmplen,
@@ -757,7 +796,13 @@ genServiceDesc(int * len, const struct serviceDesc * s)
{
str = strcat_str(str, len, &tmplen,
"<stateVariable sendEvents=\"");
+#ifdef ENABLE_EVENTS
str = strcat_str(str, len, &tmplen, (vars[i].itype & 0x80)?"yes":"no");
+#else
+ /* for the moment allways send no. Wait for SUBSCRIBE implementation
+ * before setting it to yes */
+ str = strcat_str(str, len, &tmplen, "no");
+#endif
str = strcat_str(str, len, &tmplen, "\"><name>");
str = strcat_str(str, len, &tmplen, vars[i].name);
str = strcat_str(str, len, &tmplen, "</name><dataType>");
@@ -815,42 +860,40 @@ genX_MS_MediaReceiverRegistrar(int * len)
return genServiceDesc(len, &scpdX_MS_MediaReceiverRegistrar);
}
+#ifdef ENABLE_EVENTS
static char *
genEventVars(int * len, const struct serviceDesc * s, const char * servns)
{
const struct stateVar * v;
char * str;
int tmplen;
- char buf[512];
tmplen = 512;
str = (char *)malloc(tmplen);
if(str == NULL)
return NULL;
*len = 0;
v = s->serviceStateTable;
- snprintf(buf, sizeof(buf), "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\" xmlns:s=\"%s\">", servns);
- str = strcat_str(str, len, &tmplen, buf);
+ str = strcat_str(str, len, &tmplen, "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\" xmlns:s=\"");
+ str = strcat_str(str, len, &tmplen, servns);
+ str = strcat_str(str, len, &tmplen, "\">");
while(v->name) {
if(v->itype & 0x80) {
- snprintf(buf, sizeof(buf), "<e:property><%s>", v->name);
- str = strcat_str(str, len, &tmplen, buf);
+ str = strcat_str(str, len, &tmplen, "<e:property><");
+ str = strcat_str(str, len, &tmplen, v->name);
+ str = strcat_str(str, len, &tmplen, ">");
//printf("<e:property><s:%s>", v->name);
switch(v->ieventvalue) {
case 0:
break;
case 255: /* Magical values should go around here */
- if( strcmp(v->name, "SystemUpdateID") == 0 )
- {
- snprintf(buf, sizeof(buf), "%d", updateID);
- str = strcat_str(str, len, &tmplen, buf);
- }
break;
default:
str = strcat_str(str, len, &tmplen, upnpallowedvalues[v->ieventvalue]);
//printf("%s", upnpallowedvalues[v->ieventvalue]);
}
- snprintf(buf, sizeof(buf), "</%s></e:property>", v->name);
- str = strcat_str(str, len, &tmplen, buf);
+ str = strcat_str(str, len, &tmplen, "</");
+ str = strcat_str(str, len, &tmplen, v->name);
+ str = strcat_str(str, len, &tmplen, "></e:property>");
//printf("</s:%s></e:property>\n", v->name);
}
v++;
@@ -887,3 +930,4 @@ getVarsX_MS_MediaReceiverRegistrar(int * l)
"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1");
}
+#endif
diff --git a/upnpdescgen.c.dlna b/upnpdescgen.c.dlna
new file mode 100644
index 0000000..c17b3c9
--- /dev/null
+++ b/upnpdescgen.c.dlna
@@ -0,0 +1,944 @@
+/* $Id$ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2008 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <syslog.h>
+
+#include "config.h"
+#ifdef ENABLE_EVENTS
+#include "getifaddr.h"
+#include "upnpredirect.h"
+#endif
+#include "upnpdescgen.h"
+#include "miniupnpdpath.h"
+#include "upnpglobalvars.h"
+#include "upnpdescstrings.h"
+
+static const char * const upnptypes[] =
+{
+ "string",
+ "boolean",
+ "ui2",
+ "ui4",
+ "i4",
+ "uri"
+};
+
+static const char * const upnpdefaultvalues[] =
+{
+ 0,
+ "Unconfigured"
+};
+
+static const char * const upnpallowedvalues[] =
+{
+ 0, /* 0 */
+ "DSL", /* 1 */
+ "POTS",
+ "Cable",
+ "Ethernet",
+ 0,
+ "Up", /* 6 */
+ "Down",
+ "Initializing",
+ "Unavailable",
+ 0,
+ "TCP", /* 11 */
+ "UDP",
+ 0,
+ "Unconfigured", /* 14 */
+ "IP_Routed",
+ "IP_Bridged",
+ 0,
+ "Unconfigured", /* 18 */
+ "Connecting",
+ "Connected",
+ "PendingDisconnect",
+ "Disconnecting",
+ "Disconnected",
+ 0,
+ "ERROR_NONE", /* 25 */
+ 0,
+ "OK", /* 27 */
+ "ContentFormatMismatch",
+ "InsufficientBandwidth",
+ "UnreliableChannel",
+ "Unknown",
+ 0,
+ "Input", /* 33 */
+ "Output",
+ 0,
+ "BrowseMetadata", /* 36 */
+ "BrowseDirectChildren",
+ 0,
+ "COMPLETED", /* 39 */
+ "ERROR",
+ "IN_PROGRESS",
+ "STOPPED",
+ 0,
+ "", /* 44 */
+ 0
+};
+
+static const char xmlver[] =
+ "<?xml version=\"1.0\"?>\r\n";
+static const char root_service[] =
+ "scpd xmlns=\"urn:schemas-upnp-org:service-1-0\"";
+static const char root_device[] =
+ "root xmlns=\"urn:schemas-upnp-org:device-1-0\"";
+
+/* root Description of the UPnP Device
+ * fixed to match UPnP_IGD_InternetGatewayDevice 1.0.pdf
+ * presentationURL is only "recommended" but the router doesn't appears
+ * in "Network connections" in Windows XP if it is not present. */
+static const struct XMLElt rootDesc[] =
+{
+ {root_device, INITHELPER(1,3)},
+ {"specVersion", INITHELPER(4,2)},
+ {"/URLBase", "http://192.168.10.112:5555/"},
+ {"device", INITHELPER(6,12)},
+ {"/major", "1"},
+ {"/minor", "0"},
+ {"/deviceType", "urn:schemas-upnp-org:device:MediaServer:1"},
+ {"/friendlyName", ROOTDEV_FRIENDLYNAME}, /* required */
+ {"/manufacturer", ROOTDEV_MANUFACTURER}, /* required */
+ {"/manufacturerURL", ROOTDEV_MANUFACTURERURL}, /* optional */
+ {"/modelDescription", ROOTDEV_MODELDESCRIPTION}, /* recommended */
+ {"/modelName", ROOTDEV_MODELNAME}, /* required */
+ {"/modelNumber", modelnumber},
+ {"/modelURL", ROOTDEV_MODELURL},
+ {"/serialNumber", serialnumber},
+ {"/UDN", uuidvalue}, /* required */
+ {"serviceList", INITHELPER(18,2)},
+ {"/presentationURL", presentationurl}, /* recommended */
+ {"service", INITHELPER(20,5)},
+ {"service", INITHELPER(25,5)},
+ {"/serviceType", "urn:schemas-upnp-org:service:ContentDirectory:1"},
+ {"/serviceId", "urn:upnp-org:serviceId:ContentDirectory"},
+ {"/controlURL", CONTENTDIRECTORY_CONTROLURL},
+ {"/eventSubURL", CONTENTDIRECTORY_EVENTURL},
+ {"/SCPDURL", CONTENTDIRECTORY_PATH},
+ {"/serviceType", "urn:schemas-upnp-org:service:ConnectionManager:1"},
+ {"/serviceId", "urn:upnp-org:serviceId:ConnectionManager"},
+ {"/controlURL", CONNECTIONMGR_CONTROLURL},
+ {"/eventSubURL", CONNECTIONMGR_EVENTURL},
+ {"/SCPDURL", CONNECTIONMGR_PATH},
+ {0, 0}
+};
+
+/* WANIPCn.xml */
+/* see UPnP_IGD_WANIPConnection 1.0.pdf
+static struct XMLElt scpdWANIPCn[] =
+{
+ {root_service, {INITHELPER(1,2)}},
+ {0, {0}}
+};
+*/
+static const struct argument AddPortMappingArgs[] =
+{
+ {NULL, 1, 11},
+ {NULL, 1, 12},
+ {NULL, 1, 14},
+ {NULL, 1, 13},
+ {NULL, 1, 15},
+ {NULL, 1, 9},
+ {NULL, 1, 16},
+ {NULL, 1, 10},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetExternalIPAddressArgs[] =
+{
+ {NULL, 2, 7},
+ {NULL, 0, 0}
+};
+
+static const struct argument DeletePortMappingArgs[] =
+{
+ {NULL, 1, 11},
+ {NULL, 1, 12},
+ {NULL, 1, 14},
+ {NULL, 0, 0}
+};
+
+static const struct argument SetConnectionTypeArgs[] =
+{
+ {NULL, 1, 0},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetConnectionTypeInfoArgs[] =
+{
+ {NULL, 2, 0},
+ {NULL, 2, 1},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetStatusInfoArgs[] =
+{
+ {NULL, 2, 2},
+ {NULL, 2, 4},
+ {NULL, 2, 3},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetNATRSIPStatusArgs[] =
+{
+ {NULL, 2, 5},
+ {NULL, 2, 6},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetGenericPortMappingEntryArgs[] =
+{
+ {NULL, 1, 8},
+ {NULL, 2, 11},
+ {NULL, 2, 12},
+ {NULL, 2, 14},
+ {NULL, 2, 13},
+ {NULL, 2, 15},
+ {NULL, 2, 9},
+ {NULL, 2, 16},
+ {NULL, 2, 10},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetSpecificPortMappingEntryArgs[] =
+{
+ {NULL, 1, 11},
+ {NULL, 1, 12},
+ {NULL, 1, 14},
+ {NULL, 2, 13},
+ {NULL, 2, 15},
+ {NULL, 2, 9},
+ {NULL, 2, 16},
+ {NULL, 2, 10},
+ {NULL, 0, 0}
+};
+
+/* For ConnectionManager */
+static const struct argument GetProtocolInfoArgs[] =
+{
+ {"Source", 2, 0},
+ {"Sink", 2, 1},
+ {NULL, 0, 0}
+};
+
+static const struct argument PrepareForConnectionArgs[] =
+{
+ {"RemoteProtocolInfo", 1, 6},
+ {"PeerConnectionManager", 1, 4},
+ {"PeerConnectionID", 1, 7},
+ {"Direction", 1, 5},
+ {"ConnectionID", 2, 7},
+ {"AVTransportID", 2, 8},
+ {"RcsID", 2, 9},
+ {NULL, 0, 0}
+};
+
+static const struct argument ConnectionCompleteArgs[] =
+{
+ {"ConnectionID", 1, 7},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetCurrentConnectionIDsArgs[] =
+{
+ {"ConnectionIDs", 2, 2},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetCurrentConnectionInfoArgs[] =
+{
+ {"ConnectionID", 1, 7},
+ {"RcsID", 2, 9},
+ {"AVTransportID", 2, 8},
+ {"ProtocolInfo", 2, 6},
+ {"PeerConnectionManager", 2, 4},
+ {"PeerConnectionID", 2, 7},
+ {"Direction", 2, 5},
+ {"Status", 2, 3},
+ {NULL, 0, 0}
+};
+
+static const struct action ConnectionManagerActions[] =
+{
+ {"GetProtocolInfo", GetProtocolInfoArgs}, /* R */
+ {"PrepareForConnection", PrepareForConnectionArgs}, /* R */
+ {"ConnectionComplete", ConnectionCompleteArgs}, /* R */
+ {"GetCurrentConnectionIDs", GetCurrentConnectionIDsArgs}, /* R */
+ {"GetCurrentConnectionInfo", GetCurrentConnectionInfoArgs}, /* R */
+ {0, 0}
+};
+
+static const struct stateVar ConnectionManagerVars[] =
+{
+ {"SourceProtocolInfo", 1<<7, 0}, /* required */
+ {"SinkProtocolInfo", 1<<7, 0}, /* required */
+ {"CurrentConnectionIDs", 1<<7, 0}, /* required */
+ {"A_ARG_TYPE_ConnectionStatus", 0, 0, 27}, /* required */
+ {"A_ARG_TYPE_ConnectionManager", 0, 0}, /* required */
+ {"A_ARG_TYPE_Direction", 0, 0, 33}, /* required */
+ {"A_ARG_TYPE_ProtocolInfo", 0, 0}, /* required */
+ {"A_ARG_TYPE_ConnectionID", 4, 0}, /* required */
+ {"A_ARG_TYPE_AVTransportID", 4, 0}, /* required */
+ {"A_ARG_TYPE_RcsID", 4, 0}, /* required */
+ {0, 0}
+};
+
+static const struct argument GetSearchCapabilitiesArgs[] =
+{
+ {"SearchCaps", 2, 16},
+ {0, 0}
+};
+
+static const struct argument GetSortCapabilitiesArgs[] =
+{
+ {"SortCaps", 2, 17},
+ {0, 0}
+};
+
+static const struct argument GetSystemUpdateIDArgs[] =
+{
+ {"Id", 2, 18},
+ {0, 0}
+};
+
+static const struct argument BrowseArgs[] =
+{
+ {"ObjectID", 1, 1},
+ {"BrowseFlag", 1, 4},
+ {"Filter", 1, 5},
+ {"StartingIndex", 1, 7},
+ {"RequestedCount", 1, 8},
+ {"SortCriteria", 1, 6},
+ {"Result", 2, 2},
+ {"NumberReturned", 2, 8},
+ {"TotalMatches", 2, 8},
+ {"UpdateID", 2, 9},
+ {0, 0}
+};
+
+static const struct action ContentDirectoryActions[] =
+{
+ {"GetSearchCapabilities", GetSearchCapabilitiesArgs}, /* R */
+ {"GetSortCapabilities", GetSortCapabilitiesArgs}, /* R */
+ {"GetSystemUpdateID", GetSystemUpdateIDArgs}, /* R */
+ {"Browse", BrowseArgs}, /* R */
+#if 0 // Not implementing optional features yet...
+ {"Search", SearchArgs}, /* O */
+ {"CreateObject", CreateObjectArgs}, /* O */
+ {"DestroyObject", DestroyObjectArgs}, /* O */
+ {"UpdateObject", UpdateObjectArgs}, /* O */
+ {"ImportResource", ImportResourceArgs}, /* O */
+ {"ExportResource", ExportResourceArgs}, /* O */
+ {"StopTransferResource", StopTransferResourceArgs}, /* O */
+ {"GetTransferProgress", GetTransferProgressArgs}, /* O */
+ {"DeleteResource", DeleteResourceArgs}, /* O */
+ {"CreateReference", CreateReferenceArgs}, /* O */
+#endif
+ {0, 0}
+};
+
+static const struct stateVar ContentDirectoryVars[] =
+{
+ {"TransferIDs", 1<<7, 0}, /* 0 */
+ {"A_ARG_TYPE_ObjectID", 0, 0},
+ {"A_ARG_TYPE_Result", 0, 0},
+ {"A_ARG_TYPE_SearchCriteria", 0, 0},
+ {"A_ARG_TYPE_BrowseFlag", 0, 0, 36},
+ /* Allowed Values : BrowseMetadata / BrowseDirectChildren */
+ {"A_ARG_TYPE_Filter", 0, 0}, /* 5 */
+ {"A_ARG_TYPE_SortCriteria", 0, 0},
+ {"A_ARG_TYPE_Index", 3, 0},
+ {"A_ARG_TYPE_Count", 3, 0},
+ {"A_ARG_TYPE_UpdateID", 3, 0},
+ {"A_ARG_TYPE_TransferID", 3, 0}, /* 10 */
+ {"A_ARG_TYPE_TransferStatus", 0, 0, 39},
+ /* Allowed Values : COMPLETED / ERROR / IN_PROGRESS / STOPPED */
+ {"A_ARG_TYPE_TransferLength", 0, 0},
+ {"A_ARG_TYPE_TransferTotal", 0, 0},
+ {"A_ARG_TYPE_TagValueList", 0, 0},
+ {"A_ARG_TYPE_URI", 5, 0}, /* 15 */
+ {"SearchCapabilities", 0, 0},
+ {"SortCapabilities", 0, 0},
+ {"SystemUpdateID", 3, 0},
+ {"ContainerUpdateIDs", 0, 0},
+ {0, 0}
+};
+
+static const struct action WANIPCnActions[] =
+{
+ {"AddPortMapping", AddPortMappingArgs}, /* R */
+ {"GetExternalIPAddress", GetExternalIPAddressArgs}, /* R */
+ {"DeletePortMapping", DeletePortMappingArgs}, /* R */
+ {"SetConnectionType", SetConnectionTypeArgs}, /* R */
+ {"GetConnectionTypeInfo", GetConnectionTypeInfoArgs}, /* R */
+ {"RequestConnection", 0}, /* R */
+ {"ForceTermination", 0}, /* R */
+ {"GetStatusInfo", GetStatusInfoArgs}, /* R */
+ {"GetNATRSIPStatus", GetNATRSIPStatusArgs}, /* R */
+ {"GetGenericPortMappingEntry", GetGenericPortMappingEntryArgs}, /* R */
+ {"GetSpecificPortMappingEntry", GetSpecificPortMappingEntryArgs}, /* R */
+ {0, 0}
+};
+/* R=Required, O=Optional */
+
+static const struct stateVar WANIPCnVars[] =
+{
+/* 0 */
+ {"ConnectionType", 0, 0/*1*/}, /* required */
+ {"PossibleConnectionTypes", 0|0x80, 0, 14, 15},
+ /* Required
+ * Allowed values : Unconfigured / IP_Routed / IP_Bridged */
+ {"ConnectionStatus", 0|0x80, 0/*1*/, 18, 20}, /* required */
+ /* Allowed Values : Unconfigured / Connecting(opt) / Connected
+ * PendingDisconnect(opt) / Disconnecting (opt)
+ * Disconnected */
+ {"Uptime", 3, 0}, /* Required */
+ {"LastConnectionError", 0, 0, 25}, /* required : */
+ /* Allowed Values : ERROR_NONE(req) / ERROR_COMMAND_ABORTED(opt)
+ * ERROR_NOT_ENABLED_FOR_INTERNET(opt)
+ * ERROR_USER_DISCONNECT(opt)
+ * ERROR_ISP_DISCONNECT(opt)
+ * ERROR_IDLE_DISCONNECT(opt)
+ * ERROR_FORCED_DISCONNECT(opt)
+ * ERROR_NO_CARRIER(opt)
+ * ERROR_IP_CONFIGURATION(opt)
+ * ERROR_UNKNOWN(opt) */
+ {"RSIPAvailable", 1, 0}, /* required */
+ {"NATEnabled", 1, 0}, /* required */
+ {"ExternalIPAddress", 0|0x80, 0, 0, 254}, /* required. Default : empty string */
+ {"PortMappingNumberOfEntries", 2|0x80, 0, 0, 253}, /* required >= 0 */
+ {"PortMappingEnabled", 1, 0}, /* Required */
+ {"PortMappingLeaseDuration", 3, 0}, /* required */
+ {"RemoteHost", 0, 0}, /* required. Default : empty string */
+ {"ExternalPort", 2, 0}, /* required */
+ {"InternalPort", 2, 0}, /* required */
+ {"PortMappingProtocol", 0, 0, 11}, /* required allowedValues: TCP/UDP */
+ {"InternalClient", 0, 0}, /* required */
+ {"PortMappingDescription", 0, 0}, /* required default: empty string */
+ {0, 0}
+};
+
+static const struct serviceDesc scpdWANIPCn =
+{ WANIPCnActions, WANIPCnVars };
+
+/* WANCfg.xml */
+/* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */
+
+static const struct argument GetCommonLinkPropertiesArgs[] =
+{
+ {NULL, 2, 0},
+ {NULL, 2, 1},
+ {NULL, 2, 2},
+ {NULL, 2, 3},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetTotalBytesSentArgs[] =
+{
+ {NULL, 2, 4},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetTotalBytesReceivedArgs[] =
+{
+ {NULL, 2, 5},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetTotalPacketsSentArgs[] =
+{
+ {NULL, 2, 6},
+ {NULL, 0, 0}
+};
+
+static const struct argument GetTotalPacketsReceivedArgs[] =
+{
+ {NULL, 2, 7},
+ {NULL, 0, 0}
+};
+
+static const struct action WANCfgActions[] =
+{
+ {"GetCommonLinkProperties", GetCommonLinkPropertiesArgs}, /* Required */
+ {"GetTotalBytesSent", GetTotalBytesSentArgs}, /* optional */
+ {"GetTotalBytesReceived", GetTotalBytesReceivedArgs}, /* optional */
+ {"GetTotalPacketsSent", GetTotalPacketsSentArgs}, /* optional */
+ {"GetTotalPacketsReceived", GetTotalPacketsReceivedArgs}, /* optional */
+ {0, 0}
+};
+
+/* See UPnP_IGD_WANCommonInterfaceConfig 1.0.pdf */
+static const struct stateVar WANCfgVars[] =
+{
+ {"WANAccessType", 0, 0, 1},
+ /* Allowed Values : DSL / POTS / Cable / Ethernet
+ * Default value : empty string */
+ {"Layer1UpstreamMaxBitRate", 3, 0},
+ {"Layer1DownstreamMaxBitRate", 3, 0},
+ {"PhysicalLinkStatus", 0|0x80, 0, 6, 6},
+ /* allowed values :
+ * Up / Down / Initializing (optional) / Unavailable (optionnal)
+ * no Default value
+ * Evented */
+ {"TotalBytesSent", 3, 0}, /* Optional */
+ {"TotalBytesReceived", 3, 0}, /* Optional */
+ {"TotalPacketsSent", 3, 0}, /* Optional */
+ {"TotalPacketsReceived", 3, 0},/* Optional */
+ /*{"MaximumActiveConnections", 2, 0}, // allowed Range value // OPTIONAL */
+ {0, 0}
+};
+
+static const struct serviceDesc scpdWANCfg =
+{ WANCfgActions, WANCfgVars };
+
+#ifdef ENABLE_L3F_SERVICE
+/* Read UPnP_IGD_Layer3Forwarding_1.0.pdf */
+static const struct argument SetDefaultConnectionServiceArgs[] =
+{
+ {NULL, 1, 0}, /* in */
+ {NULL, 0, 0}
+};
+
+static const struct argument GetDefaultConnectionServiceArgs[] =
+{
+ {NULL, 2, 0}, /* out */
+ {0, 0}
+};
+
+static const struct action L3FActions[] =
+{
+ {"SetDefaultConnectionService", SetDefaultConnectionServiceArgs}, /* Req */
+ {"GetDefaultConnectionService", GetDefaultConnectionServiceArgs}, /* Req */
+ {0, 0}
+};
+
+static const struct stateVar L3FVars[] =
+{
+ {"DefaultConnectionService", 0|0x80, 0, 0, 255}, /* Required */
+ {0, 0}
+};
+
+static const struct serviceDesc scpdL3F =
+{ L3FActions, L3FVars };
+#endif
+
+static const struct serviceDesc scpdContentDirectory =
+{ ContentDirectoryActions, ContentDirectoryVars };
+//{ ContentDirectoryActions, ContentDirectoryVars };
+
+static const struct serviceDesc scpdConnectionManager =
+{ ConnectionManagerActions, ConnectionManagerVars };
+
+/* strcat_str()
+ * concatenate the string and use realloc to increase the
+ * memory buffer if needed. */
+static char *
+strcat_str(char * str, int * len, int * tmplen, const char * s2)
+{
+ int s2len;
+ s2len = (int)strlen(s2);
+ if(*tmplen <= (*len + s2len))
+ {
+ if(s2len < 256)
+ *tmplen += 256;
+ else
+ *tmplen += s2len + 1;
+ str = (char *)realloc(str, *tmplen);
+ }
+ /*strcpy(str + *len, s2); */
+ memcpy(str + *len, s2, s2len + 1);
+ *len += s2len;
+ return str;
+}
+
+/* strcat_char() :
+ * concatenate a character and use realloc to increase the
+ * size of the memory buffer if needed */
+static char *
+strcat_char(char * str, int * len, int * tmplen, char c)
+{
+ if(*tmplen <= (*len + 1))
+ {
+ *tmplen += 256;
+ str = (char *)realloc(str, *tmplen);
+ }
+ str[*len] = c;
+ (*len)++;
+ return str;
+}
+
+/* iterative subroutine using a small stack
+ * This way, the progam stack usage is kept low */
+static char *
+genXML(char * str, int * len, int * tmplen,
+ const struct XMLElt * p)
+{
+ unsigned short i, j, k;
+ int top;
+ const char * eltname, *s;
+ char c;
+ struct {
+ unsigned short i;
+ unsigned short j;
+ const char * eltname;
+ } pile[16]; /* stack */
+ top = -1;
+ i = 0; /* current node */
+ j = 1; /* i + number of nodes*/
+ for(;;)
+ {
+ eltname = p[i].eltname;
+ if(!eltname)
+ return str;
+ if(eltname[0] == '/')
+ {
+ /*printf("<%s>%s<%s>\n", eltname+1, p[i].data, eltname); */
+ str = strcat_char(str, len, tmplen, '<');
+ str = strcat_str(str, len, tmplen, eltname+1);
+ str = strcat_char(str, len, tmplen, '>');
+ str = strcat_str(str, len, tmplen, p[i].data);
+ str = strcat_char(str, len, tmplen, '<');
+ str = strcat_str(str, len, tmplen, eltname);
+ str = strcat_char(str, len, tmplen, '>');
+ for(;;)
+ {
+ if(top < 0)
+ return str;
+ i = ++(pile[top].i);
+ j = pile[top].j;
+ /*printf(" pile[%d]\t%d %d\n", top, i, j); */
+ if(i==j)
+ {
+ /*printf("</%s>\n", pile[top].eltname); */
+ str = strcat_char(str, len, tmplen, '<');
+ str = strcat_char(str, len, tmplen, '/');
+ s = pile[top].eltname;
+ for(c = *s; c > ' '; c = *(++s))
+ str = strcat_char(str, len, tmplen, c);
+ str = strcat_char(str, len, tmplen, '>');
+ top--;
+ }
+ else
+ break;
+ }
+ }
+ else
+ {
+ /*printf("<%s>\n", eltname); */
+ str = strcat_char(str, len, tmplen, '<');
+ str = strcat_str(str, len, tmplen, eltname);
+ str = strcat_char(str, len, tmplen, '>');
+ k = i;
+ /*i = p[k].index; */
+ /*j = i + p[k].nchild; */
+ i = (unsigned)p[k].data & 0xffff;
+ j = i + ((unsigned)p[k].data >> 16);
+ top++;
+ /*printf(" +pile[%d]\t%d %d\n", top, i, j); */
+ pile[top].i = i;
+ pile[top].j = j;
+ pile[top].eltname = eltname;
+ }
+ }
+}
+
+/* genRootDesc() :
+ * - Generate the root description of the UPnP device.
+ * - the len argument is used to return the length of
+ * the returned string.
+ * - tmp_uuid argument is used to build the uuid string */
+char *
+genRootDesc(int * len)
+{
+ char * str;
+ int tmplen;
+ tmplen = 2048;
+ str = (char *)malloc(tmplen);
+ if(str == NULL)
+ return NULL;
+ * len = strlen(xmlver);
+ /*strcpy(str, xmlver); */
+ memcpy(str, xmlver, *len + 1);
+ str = genXML(str, len, &tmplen, rootDesc);
+ str[*len] = '\0';
+ return str;
+}
+
+/* genServiceDesc() :
+ * Generate service description with allowed methods and
+ * related variables. */
+static char *
+genServiceDesc(int * len, const struct serviceDesc * s)
+{
+ int i, j;
+ const struct action * acts;
+ const struct stateVar * vars;
+ const struct argument * args;
+ const char * p;
+ char * str;
+ int tmplen;
+ tmplen = 2048;
+ str = (char *)malloc(tmplen);
+ if(str == NULL)
+ return NULL;
+ /*strcpy(str, xmlver); */
+ *len = strlen(xmlver);
+ memcpy(str, xmlver, *len + 1);
+
+ acts = s->actionList;
+ vars = s->serviceStateTable;
+
+ str = strcat_char(str, len, &tmplen, '<');
+ str = strcat_str(str, len, &tmplen, root_service);
+ str = strcat_char(str, len, &tmplen, '>');
+
+ str = strcat_str(str, len, &tmplen,
+ "<specVersion><major>1</major><minor>0</minor></specVersion>");
+
+ i = 0;
+ str = strcat_str(str, len, &tmplen, "<actionList>");
+ while(acts[i].name)
+ {
+ str = strcat_str(str, len, &tmplen, "<action><name>");
+ str = strcat_str(str, len, &tmplen, acts[i].name);
+ str = strcat_str(str, len, &tmplen, "</name>");
+ /* argument List */
+ args = acts[i].args;
+ if(args)
+ {
+ str = strcat_str(str, len, &tmplen, "<argumentList>");
+ j = 0;
+ while(args[j].dir)
+ {
+ //JM str = strcat_str(str, len, &tmplen, "<argument><name>New");
+ str = strcat_str(str, len, &tmplen, "<argument><name>");
+ p = vars[args[j].relatedVar].name;
+ if(0 == memcmp(p, "PortMapping", 11)
+ && 0 != memcmp(p + 11, "Description", 11)) {
+ if(0 == memcmp(p + 11, "NumberOfEntries", 15))
+ str = strcat_str(str, len, &tmplen, "PortMappingIndex");
+ else
+ str = strcat_str(str, len, &tmplen, p + 11);
+ } else {
+ str = strcat_str(str, len, &tmplen, (args[j].name ? args[j].name : p));
+ }
+ str = strcat_str(str, len, &tmplen, "</name><direction>");
+ str = strcat_str(str, len, &tmplen, args[j].dir==1?"in":"out");
+ str = strcat_str(str, len, &tmplen,
+ "</direction><relatedStateVariable>");
+ str = strcat_str(str, len, &tmplen, p);
+ str = strcat_str(str, len, &tmplen,
+ "</relatedStateVariable></argument>");
+ j++;
+ }
+ str = strcat_str(str, len, &tmplen,"</argumentList>");
+ }
+ str = strcat_str(str, len, &tmplen, "</action>");
+ /*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */
+ i++;
+ }
+ str = strcat_str(str, len, &tmplen, "</actionList><serviceStateTable>");
+ i = 0;
+ while(vars[i].name)
+ {
+ str = strcat_str(str, len, &tmplen,
+ "<stateVariable sendEvents=\"");
+#ifdef ENABLE_EVENTS
+ str = strcat_str(str, len, &tmplen, (vars[i].itype & 0x80)?"yes":"no");
+#else
+ /* for the moment allways send no. Wait for SUBSCRIBE implementation
+ * before setting it to yes */
+ str = strcat_str(str, len, &tmplen, "no");
+#endif
+ str = strcat_str(str, len, &tmplen, "\"><name>");
+ str = strcat_str(str, len, &tmplen, vars[i].name);
+ str = strcat_str(str, len, &tmplen, "</name><dataType>");
+ str = strcat_str(str, len, &tmplen, upnptypes[vars[i].itype & 0x0f]);
+ str = strcat_str(str, len, &tmplen, "</dataType>");
+ if(vars[i].iallowedlist)
+ {
+ str = strcat_str(str, len, &tmplen, "<allowedValueList>");
+ for(j=vars[i].iallowedlist; upnpallowedvalues[j]; j++)
+ {
+ str = strcat_str(str, len, &tmplen, "<allowedValue>");
+ str = strcat_str(str, len, &tmplen, upnpallowedvalues[j]);
+ str = strcat_str(str, len, &tmplen, "</allowedValue>");
+ }
+ str = strcat_str(str, len, &tmplen, "</allowedValueList>");
+ }
+ /*if(vars[i].defaultValue) */
+ if(vars[i].idefault)
+ {
+ str = strcat_str(str, len, &tmplen, "<defaultValue>");
+ /*str = strcat_str(str, len, &tmplen, vars[i].defaultValue); */
+ str = strcat_str(str, len, &tmplen, upnpdefaultvalues[vars[i].idefault]);
+ str = strcat_str(str, len, &tmplen, "</defaultValue>");
+ }
+ str = strcat_str(str, len, &tmplen, "</stateVariable>");
+ /*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */
+ i++;
+ }
+ str = strcat_str(str, len, &tmplen, "</serviceStateTable></scpd>");
+ str[*len] = '\0';
+ return str;
+}
+
+/* genContentDirectory() :
+ * Generate the ContentDirectory xml description */
+char *
+genContentDirectory(int * len)
+{
+ return genServiceDesc(len, &scpdContentDirectory);
+}
+
+/* genConnectionManager() :
+ * Generate the ConnectionManager xml description */
+char *
+genConnectionManager(int * len)
+{
+ return genServiceDesc(len, &scpdConnectionManager);
+}
+
+/* genWANIPCn() :
+ * Generate the WANIPConnection xml description */
+char *
+genWANIPCn(int * len)
+{
+ return genServiceDesc(len, &scpdWANIPCn);
+}
+
+/* genWANCfg() :
+ * Generate the WANInterfaceConfig xml description. */
+char *
+genWANCfg(int * len)
+{
+ return genServiceDesc(len, &scpdWANCfg);
+}
+
+#ifdef ENABLE_L3F_SERVICE
+char *
+genL3F(int * len)
+{
+ return genServiceDesc(len, &scpdL3F);
+}
+#endif
+
+#ifdef ENABLE_EVENTS
+static char *
+genEventVars(int * len, const struct serviceDesc * s, const char * servns)
+{
+ char tmp[16];
+ const struct stateVar * v;
+ char * str;
+ int tmplen;
+ tmplen = 512;
+ str = (char *)malloc(tmplen);
+ if(str == NULL)
+ return NULL;
+ *len = 0;
+ v = s->serviceStateTable;
+ str = strcat_str(str, len, &tmplen, "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\" xmlns:s=\"");
+ str = strcat_str(str, len, &tmplen, servns);
+ str = strcat_str(str, len, &tmplen, "\">");
+ while(v->name) {
+ if(v->itype & 0x80) {
+ str = strcat_str(str, len, &tmplen, "<e:property><s:");
+ str = strcat_str(str, len, &tmplen, v->name);
+ str = strcat_str(str, len, &tmplen, ">");
+ //printf("<e:property><s:%s>", v->name);
+ switch(v->ieventvalue) {
+ case 0:
+ break;
+ case 253: /* Port mapping number of entries magical value */
+ snprintf(tmp, sizeof(tmp), "%d", upnp_get_portmapping_number_of_entries());
+ str = strcat_str(str, len, &tmplen, tmp);
+ break;
+ case 254: /* External ip address magical value */
+ if(use_ext_ip_addr)
+ str = strcat_str(str, len, &tmplen, use_ext_ip_addr);
+ else {
+ char ext_ip_addr[INET_ADDRSTRLEN];
+ if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) {
+ str = strcat_str(str, len, &tmplen, "0.0.0.0");
+ } else {
+ str = strcat_str(str, len, &tmplen, ext_ip_addr);
+ }
+ }
+ /*str = strcat_str(str, len, &tmplen, "0.0.0.0");*/
+ break;
+ case 255: /* DefaultConnectionService magical value */
+ str = strcat_str(str, len, &tmplen, uuidvalue);
+ str = strcat_str(str, len, &tmplen, ":WANConnectionDevice:1,urn:upnp-org:serviceId:WANIPConn1");
+ //printf("%s:WANConnectionDevice:1,urn:upnp-org:serviceId:WANIPConn1", uuidvalue);
+ break;
+ default:
+ str = strcat_str(str, len, &tmplen, upnpallowedvalues[v->ieventvalue]);
+ //printf("%s", upnpallowedvalues[v->ieventvalue]);
+ }
+ str = strcat_str(str, len, &tmplen, "</s:");
+ str = strcat_str(str, len, &tmplen, v->name);
+ str = strcat_str(str, len, &tmplen, "></e:property>");
+ //printf("</s:%s></e:property>\n", v->name);
+ }
+ v++;
+ }
+ str = strcat_str(str, len, &tmplen, "</e:propertyset>");
+ //printf("</e:propertyset>\n");
+ //printf("\n");
+ //printf("%d\n", tmplen);
+ str[*len] = '\0';
+ return str;
+}
+
+char *
+getVarsContentDirectory(int * l)
+{
+ return genEventVars(l,
+ &scpdContentDirectory,
+ "urn:schemas-upnp-org:service:ContentDirectory:1");
+}
+
+char *
+getVarsConnectionManager(int * l)
+{
+ return genEventVars(l,
+ &scpdConnectionManager,
+ "urn:schemas-upnp-org:service:ConnectionManager:1");
+}
+
+char *
+getVarsWANIPCn(int * l)
+{
+ return genEventVars(l,
+ &scpdWANIPCn,
+ "urn:schemas-upnp-org:service:WANIPConnection:1");
+}
+
+char *
+getVarsWANCfg(int * l)
+{
+ return genEventVars(l,
+ &scpdWANCfg,
+ "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1");
+}
+
+#ifdef ENABLE_L3F_SERVICE
+char *
+getVarsL3F(int * l)
+{
+ return genEventVars(l,
+ &scpdL3F,
+ "urn:schemas-upnp-org:service:Layer3Forwarding:1");
+}
+#endif
+#endif
diff --git a/upnpdescgen.h b/upnpdescgen.h
index edfd9f5..f84da77 100644
--- a/upnpdescgen.h
+++ b/upnpdescgen.h
@@ -1,31 +1,9 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006-2008, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006-2008 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#ifndef __UPNPDESCGEN_H__
#define __UPNPDESCGEN_H__
@@ -85,13 +63,32 @@ char *
genX_MS_MediaReceiverRegistrar(int * len);
char *
+genWANIPCn(int * len);
+
+char *
+genWANCfg(int * len);
+
+#ifdef ENABLE_L3F_SERVICE
+char *
+genL3F(int * len);
+#endif
+
+#ifdef ENABLE_EVENTS
+char *
getVarsContentDirectory(int * len);
char *
getVarsConnectionManager(int * len);
char *
-getVarsX_MS_MediaReceiverRegistrar(int * len);
+getVarsWANIPCn(int * len);
+
+char *
+getVarsWANCfg(int * len);
+
+char *
+getVarsL3F(int * len);
+#endif
#endif
diff --git a/upnpdescstrings.h b/upnpdescstrings.h
index 991f532..57b543e 100644
--- a/upnpdescstrings.h
+++ b/upnpdescstrings.h
@@ -1,53 +1,38 @@
/* miniupnp project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the coditions detailed in
+ * the LICENCE file provided within the distribution */
#ifndef __UPNPDESCSTRINGS_H__
#define __UPNPDESCSTRINGS_H__
#include "config.h"
/* strings used in the root device xml description */
-#ifdef NETGEAR
- #define ROOTDEV_MANUFACTURERURL "http://www.netgear.com/"
- #define ROOTDEV_MANUFACTURER "NETGEAR"
- #define ROOTDEV_MODELNAME "Windows Media Connect compatible (ReadyDLNA)"
- #define ROOTDEV_MODELURL OS_URL
- #ifdef READYNAS
- #define ROOTDEV_MODELDESCRIPTION "ReadyDLNA on ReadyNAS RAIDiator OS"
- #else
- #define ROOTDEV_MODELDESCRIPTION "ReadyDLNA"
- #endif
-#else
- #define ROOTDEV_MANUFACTURERURL OS_URL
- #define ROOTDEV_MANUFACTURER "Justin Maggard"
- #define ROOTDEV_MODELNAME "Windows Media Connect compatible (MiniDLNA)"
- #define ROOTDEV_MODELDESCRIPTION "MiniDLNA on " OS_NAME
- #define ROOTDEV_MODELURL OS_URL
-#endif
+#define ROOTDEV_FRIENDLYNAME "MiniDLNA ReadyNAS:"
+#define ROOTDEV_MANUFACTURER "NETGEAR"
+#define ROOTDEV_MANUFACTURERURL OS_URL
+#define ROOTDEV_MODELNAME "Windows Media Connect compatible (minidlna)"
+#define ROOTDEV_MODELDESCRIPTION OS_NAME " *ReadyNAS dev DLNA"
+#define ROOTDEV_MODELURL OS_URL
+
+#define WANDEV_FRIENDLYNAME "WANDevice"
+#define WANDEV_MANUFACTURER "MiniUPnP"
+#define WANDEV_MANUFACTURERURL "http://miniupnp.free.fr/"
+#define WANDEV_MODELNAME "WAN Device"
+#define WANDEV_MODELDESCRIPTION "WAN Device"
+#define WANDEV_MODELNUMBER UPNP_VERSION
+#define WANDEV_MODELURL "http://miniupnp.free.fr/"
+#define WANDEV_UPC "MINIUPNPD"
+
+#define WANCDEV_FRIENDLYNAME "WANConnectionDevice"
+#define WANCDEV_MANUFACTURER WANDEV_MANUFACTURER
+#define WANCDEV_MANUFACTURERURL WANDEV_MANUFACTURERURL
+#define WANCDEV_MODELNAME "MiniUPnPd"
+#define WANCDEV_MODELDESCRIPTION "MiniUPnP daemon"
+#define WANCDEV_MODELNUMBER UPNP_VERSION
+#define WANCDEV_MODELURL "http://miniupnp.free.fr/"
+#define WANCDEV_UPC "MINIUPNPD"
#endif
+
diff --git a/upnpevents.c b/upnpevents.c
index e9f8e08..07e06fb 100644
--- a/upnpevents.c
+++ b/upnpevents.c
@@ -1,53 +1,13 @@
-/* MiniDLNA project
- * http://minidlna.sourceforge.net/
- *
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- *
- * Portions of the code from the MiniUPnP project:
- *
- * Copyright (c) 2006-2007, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+/* $Id$ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2008 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#include <stdio.h>
#include <string.h>
-#include <errno.h>
+#include <syslog.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <unistd.h>
@@ -58,14 +18,18 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
-
#include "config.h"
#include "upnpevents.h"
-#include "minidlnapath.h"
+#include "miniupnpdpath.h"
#include "upnpglobalvars.h"
#include "upnpdescgen.h"
-#include "uuid.h"
-#include "log.h"
+
+#ifdef ENABLE_EVENTS
+/*enum subscriber_service_enum {
+ EWanCFG = 1,
+ EWanIPC,
+ EL3F
+};*/
/* stuctures definitions */
struct subscriber {
@@ -116,12 +80,18 @@ newSubscriber(const char * eventurl, const char * callback, int callbacklen)
if(!eventurl || !callback || !callbacklen)
return NULL;
tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1);
- if(strcmp(eventurl, CONTENTDIRECTORY_EVENTURL)==0)
+ if(strcmp(eventurl, WANCFG_EVENTURL)==0)
+ tmp->service = EWanCFG;
+ else if(strcmp(eventurl, CONTENTDIRECTORY_EVENTURL)==0)
tmp->service = EContentDirectory;
else if(strcmp(eventurl, CONNECTIONMGR_EVENTURL)==0)
tmp->service = EConnectionManager;
- else if(strcmp(eventurl, X_MS_MEDIARECEIVERREGISTRAR_EVENTURL)==0)
- tmp->service = EMSMediaReceiverRegistrar;
+ else if(strcmp(eventurl, WANIPC_EVENTURL)==0)
+ tmp->service = EWanIPC;
+#ifdef ENABLE_L3F_SERVICE
+ else if(strcmp(eventurl, L3F_EVENTURL)==0)
+ tmp->service = EL3F;
+#endif
else {
free(tmp);
return NULL;
@@ -129,13 +99,10 @@ newSubscriber(const char * eventurl, const char * callback, int callbacklen)
memcpy(tmp->callback, callback, callbacklen);
tmp->callback[callbacklen] = '\0';
/* make a dummy uuid */
+ /* TODO: improve that */
strncpy(tmp->uuid, uuidvalue, sizeof(tmp->uuid));
- if( get_uuid_string(tmp->uuid+5) != 0 )
- {
- tmp->uuid[sizeof(tmp->uuid)-1] = '\0';
- snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff);
- }
-
+ tmp->uuid[sizeof(tmp->uuid)-1] = '\0';
+ snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff);
return tmp;
}
@@ -149,7 +116,7 @@ upnpevents_addSubscriber(const char * eventurl,
struct subscriber * tmp;
/*static char uuid[42];*/
/* "uuid:00000000-0000-0000-0000-000000000000"; 5+36+1=42bytes */
- DPRINTF(E_DEBUG, L_HTTP, "addSubscriber(%s, %.*s, %d)\n",
+ syslog(LOG_DEBUG, "addSubscriber(%s, %.*s, %d)",
eventurl, callbacklen, callback, timeout);
/*strncpy(uuid, uuidvalue, sizeof(uuid));
uuid[sizeof(uuid)-1] = '\0';*/
@@ -169,7 +136,7 @@ renewSubscription(const char * sid, int sidlen, int timeout)
{
struct subscriber * sub;
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
- if(memcmp(sid, sub->uuid, 41) == 0) {
+ if(memcmp(sid, sub->uuid, 41)) {
sub->timeout = (timeout ? time(NULL) + timeout : 0);
return 0;
}
@@ -183,10 +150,8 @@ upnpevents_removeSubscriber(const char * sid, int sidlen)
struct subscriber * sub;
if(!sid)
return -1;
- DPRINTF(E_DEBUG, L_HTTP, "removeSubscriber(%.*s)\n",
- sidlen, sid);
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
- if(memcmp(sid, sub->uuid, 41) == 0) {
+ if(memcmp(sid, sub->uuid, 41)) {
if(sub->notify) {
sub->notify->sub = NULL;
}
@@ -218,24 +183,24 @@ upnp_event_create_notify(struct subscriber * sub)
int flags;
obj = calloc(1, sizeof(struct upnp_event_notify));
if(!obj) {
- DPRINTF(E_ERROR, L_HTTP, "%s: calloc(): %s\n", "upnp_event_create_notify", strerror(errno));
+ syslog(LOG_ERR, "%s: calloc(): %m", "upnp_event_create_notify");
return;
}
obj->sub = sub;
obj->state = ECreated;
obj->s = socket(PF_INET, SOCK_STREAM, 0);
if(obj->s<0) {
- DPRINTF(E_ERROR, L_HTTP, "%s: socket(): %s\n", "upnp_event_create_notify", strerror(errno));
+ syslog(LOG_ERR, "%s: socket(): %m", "upnp_event_create_notify");
goto error;
}
if((flags = fcntl(obj->s, F_GETFL, 0)) < 0) {
- DPRINTF(E_ERROR, L_HTTP, "%s: fcntl(..F_GETFL..): %s\n",
- "upnp_event_create_notify", strerror(errno));
+ syslog(LOG_ERR, "%s: fcntl(..F_GETFL..): %m",
+ "upnp_event_create_notify");
goto error;
}
if(fcntl(obj->s, F_SETFL, flags | O_NONBLOCK) < 0) {
- DPRINTF(E_ERROR, L_HTTP, "%s: fcntl(..F_SETFL..): %s\n",
- "upnp_event_create_notify", strerror(errno));
+ syslog(LOG_ERR, "%s: fcntl(..F_SETFL..): %m",
+ "upnp_event_create_notify");
goto error;
}
if(sub)
@@ -273,7 +238,7 @@ upnp_event_notify_connect(struct upnp_event_notify * obj)
i = 1;
p++;
port = (unsigned short)atoi(p);
- while(*p != '/' && *p != '\0') {
+ while(*p != '/') {
if(i<7) obj->portstr[i++] = *p;
p++;
}
@@ -282,19 +247,16 @@ upnp_event_notify_connect(struct upnp_event_notify * obj)
port = 80;
obj->portstr[0] = '\0';
}
- if( *p )
- obj->path = p;
- else
- obj->path = "/";
+ obj->path = p;
addr.sin_family = AF_INET;
inet_aton(obj->addrstr, &addr.sin_addr);
addr.sin_port = htons(port);
- DPRINTF(E_DEBUG, L_HTTP, "%s: '%s' %hu '%s'\n", "upnp_event_notify_connect",
+ syslog(LOG_DEBUG, "%s: '%s' %hu '%s'", "upnp_event_notify_connect",
obj->addrstr, port, obj->path);
obj->state = EConnecting;
if(connect(obj->s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if(errno != EINPROGRESS && errno != EWOULDBLOCK) {
- DPRINTF(E_ERROR, L_HTTP, "%s: connect(): %s\n", "upnp_event_notify_connect", strerror(errno));
+ syslog(LOG_ERR, "%s: connect(): %m", "upnp_event_notify_connect");
obj->state = EError;
}
}
@@ -328,38 +290,37 @@ static void upnp_event_prepare(struct upnp_event_notify * obj)
case EConnectionManager:
xml = getVarsConnectionManager(&l);
break;
- case EMSMediaReceiverRegistrar:
- xml = getVarsX_MS_MediaReceiverRegistrar(&l);
- break;
default:
xml = NULL;
l = 0;
}
- obj->tosend = asprintf(&(obj->buffer), notifymsg,
+ obj->buffersize = 1536;
+ obj->buffer = malloc(obj->buffersize);
+ /*if(!obj->buffer) {
+ }*/
+ obj->tosend = snprintf(obj->buffer, obj->buffersize, notifymsg,
obj->path, obj->addrstr, obj->portstr, l+2,
obj->sub->uuid, obj->sub->seq,
l, xml);
- obj->buffersize = obj->tosend;
if(xml) {
free(xml);
xml = NULL;
}
- DPRINTF(E_DEBUG, L_HTTP, "Sending UPnP Event response:\n%s\n", obj->buffer);
+ //DEBUG printf("Preparing buffer:\n%s\n", obj->buffer);
obj->state = ESending;
}
static void upnp_event_send(struct upnp_event_notify * obj)
{
int i;
- //DEBUG DPRINTF(E_DEBUG, L_HTTP, "Sending UPnP Event:\n%s", obj->buffer+obj->sent);
i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0);
if(i<0) {
- DPRINTF(E_WARN, L_HTTP, "%s: send(): %s\n", "upnp_event_send", strerror(errno));
+ syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send");
obj->state = EError;
return;
}
else if(i != (obj->tosend - obj->sent))
- DPRINTF(E_WARN, L_HTTP, "%s: %d bytes send out of %d\n",
+ syslog(LOG_NOTICE, "%s: %d bytes send out of %d",
"upnp_event_send", i, obj->tosend - obj->sent);
obj->sent += i;
if(obj->sent == obj->tosend)
@@ -371,11 +332,11 @@ static void upnp_event_recv(struct upnp_event_notify * obj)
int n;
n = recv(obj->s, obj->buffer, obj->buffersize, 0);
if(n<0) {
- DPRINTF(E_ERROR, L_HTTP, "%s: recv(): %s\n", "upnp_event_recv", strerror(errno));
+ syslog(LOG_ERR, "%s: recv(): %m", "upnp_event_recv");
obj->state = EError;
return;
}
- DPRINTF(E_DEBUG, L_HTTP, "%s: (%dbytes) %.*s\n", "upnp_event_recv",
+ syslog(LOG_DEBUG, "%s: (%dbytes) %.*s", "upnp_event_recv",
n, n, obj->buffer);
obj->state = EFinished;
if(obj->sub)
@@ -402,7 +363,7 @@ upnp_event_process_notify(struct upnp_event_notify * obj)
obj->s = -1;
break;
default:
- DPRINTF(E_ERROR, L_HTTP, "upnp_event_process_notify: unknown state\n");
+ syslog(LOG_ERR, "upnp_event_process_notify: unknown state");
}
}
@@ -410,7 +371,7 @@ void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd)
{
struct upnp_event_notify * obj;
for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
- DPRINTF(E_DEBUG, L_HTTP, "upnpevents_selectfds: %p %d %d\n",
+ syslog(LOG_DEBUG, "upnpevents_selectfds: %p %d %d",
obj, obj->state, obj->s);
if(obj->s >= 0) {
switch(obj->state) {
@@ -444,7 +405,7 @@ void upnpevents_processfds(fd_set *readset, fd_set *writeset)
struct subscriber * subnext;
time_t curtime;
for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
- DPRINTF(E_DEBUG, L_HTTP, "%s: %p %d %d %d %d\n",
+ syslog(LOG_DEBUG, "%s: %p %d %d %d %d",
"upnpevents_processfds", obj, obj->state, obj->s,
FD_ISSET(obj->s, readset), FD_ISSET(obj->s, writeset));
if(obj->s >= 0) {
@@ -461,13 +422,11 @@ void upnpevents_processfds(fd_set *readset, fd_set *writeset)
}
if(obj->sub)
obj->sub->notify = NULL;
-#if 0 /* Just let it time out instead of explicitly removing the subscriber */
/* remove also the subscriber from the list if there was an error */
if(obj->state == EError && obj->sub) {
LIST_REMOVE(obj->sub, entries);
free(obj->sub);
}
-#endif
if(obj->buffer) {
free(obj->buffer);
}
@@ -488,7 +447,7 @@ void upnpevents_processfds(fd_set *readset, fd_set *writeset)
}
}
-#ifdef USE_MINIDLNACTL
+#ifdef USE_MINIUPNPDCTL
void write_events_details(int s) {
int n;
char buff[80];
@@ -514,3 +473,6 @@ void write_events_details(int s) {
}
}
#endif
+
+#endif
+
diff --git a/upnpevents.h b/upnpevents.h
index c9748af..b4d2566 100644
--- a/upnpevents.h
+++ b/upnpevents.h
@@ -1,56 +1,18 @@
-/* MiniDLNA project
- * http://minidlna.sourceforge.net/
- *
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- *
- * Portions of the code from the MiniUPnP project:
- *
- * Copyright (c) 2006-2007, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2008 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#ifndef __UPNPEVENTS_H__
#define __UPNPEVENTS_H__
+#ifdef ENABLE_EVENTS
enum subscriber_service_enum {
- EContentDirectory = 1,
+ EWanCFG = 1,
+ EContentDirectory,
EConnectionManager,
- EMSMediaReceiverRegistrar
+ EWanIPC,
+ EL3F
};
void
@@ -75,3 +37,4 @@ void write_events_details(int s);
#endif
#endif
+#endif
diff --git a/upnpglobalvars.c b/upnpglobalvars.c
index d059daa..0459352 100644
--- a/upnpglobalvars.c
+++ b/upnpglobalvars.c
@@ -1,94 +1,81 @@
-/* MiniDLNA project
- *
- * http://sourceforge.net/projects/minidlna/
- *
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- *
- * Portions of the code from the MiniUPnP project:
- *
- * Copyright (c) 2006-2007, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+/* $Id$ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#include <sys/types.h>
#include <netinet/in.h>
-#include <linux/limits.h>
#include "config.h"
#include "upnpglobalvars.h"
-#include "upnpdescstrings.h"
+
+/* network interface for internet */
+const char * ext_if_name = 0;
+
+/* file to store leases */
+#ifdef ENABLE_LEASEFILE
+const char* lease_file = 0;
+#endif
+
+/* forced ip address to use for this interface
+ * when NULL, getifaddr() is used */
+const char * use_ext_ip_addr = 0;
/* LAN address */
/*const char * listen_addr = 0;*/
+unsigned long downstream_bitrate = 0;
+unsigned long upstream_bitrate = 0;
+
/* startup time */
time_t startup_time = 0;
-struct runtime_vars_s runtime_vars;
-int runtime_flags = INOTIFY_MASK;
+#if 0
+/* use system uptime */
+int sysuptime = 0;
+
+/* log packets flag */
+int logpackets = 0;
+
+#ifdef ENABLE_NATPMP
+int enablenatpmp = 0;
+#endif
+#endif
+
+int runtime_flags = 0;
const char * pidfilename = "/var/run/minidlna.pid";
char uuidvalue[] = "uuid:00000000-0000-0000-0000-000000000000";
-char modelname[MODELNAME_MAX_LEN] = ROOTDEV_MODELNAME;
-char modelnumber[MODELNUMBER_MAX_LEN] = MINIDLNA_VERSION;
char serialnumber[SERIALNUMBER_MAX_LEN] = "00000000";
-#if PNPX
-char pnpx_hwid[] = "VEN_0000&amp;DEV_0000&amp;REV_01 VEN_0033&amp;DEV_0001&amp;REV_01";
-#endif
+
+char modelnumber[MODELNUMBER_MAX_LEN] = "1";
/* presentation url :
* http://nnn.nnn.nnn.nnn:ppppp/ => max 30 bytes including terminating 0 */
char presentationurl[PRESENTATIONURL_MAX_LEN];
+/* UPnP permission rules : */
+struct upnpperm * upnppermlist = 0;
+unsigned int num_upnpperm = 0;
+
+#ifdef ENABLE_NATPMP
+/* NAT-PMP */
+unsigned int nextnatpmptoclean_timestamp = 0;
+unsigned short nextnatpmptoclean_eport = 0;
+unsigned short nextnatpmptoclean_proto = 0;
+#endif
+
+#ifdef USE_PF
+const char * queue = 0;
+const char * tag = 0;
+#endif
+
int n_lan_addr = 0;
struct lan_addr_s lan_addr[MAX_LAN_ADDR];
/* UPnP-A/V [DLNA] */
-sqlite3 * db;
-char dlna_no_conv[] = "DLNA.ORG_OP=01;DLNA.ORG_CI=0";
-char friendly_name[FRIENDLYNAME_MAX_LEN];
-char db_path[PATH_MAX] = {'\0'};
-char log_path[PATH_MAX] = {'\0'};
-struct media_dir_s * media_dirs = NULL;
-struct album_art_name_s * album_art_names = NULL;
-struct client_cache_s clients[CLIENT_CACHE_SLOTS];
-short int scanning = 0;
-volatile short int quitting = 0;
-volatile uint32_t updateID = 0;
+sqlite3 *db;
+char media_dir[256];
diff --git a/upnpglobalvars.h b/upnpglobalvars.h
index 099c7da..4d32f9f 100644
--- a/upnpglobalvars.h
+++ b/upnpglobalvars.h
@@ -1,180 +1,50 @@
-/* MiniDLNA project
- *
- * http://sourceforge.net/projects/minidlna/
- *
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- *
- * Portions of the code from the MiniUPnP project:
- *
- * Copyright (c) 2006-2007, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#ifndef __UPNPGLOBALVARS_H__
#define __UPNPGLOBALVARS_H__
#include <time.h>
-
-#include "minidlnatypes.h"
+#include "miniupnpdtypes.h"
#include "config.h"
#include <sqlite3.h>
-#define MINIDLNA_VERSION "1.0.19.2"
-
-#ifdef NETGEAR
-# define SERVER_NAME "ReadyDLNA"
-#else
-# define SERVER_NAME "MiniDLNA"
-#endif
-
-#define CLIENT_CACHE_SLOTS 20
-#define USE_FORK 1
-#define DB_VERSION 5
+/* name of the network interface used to acces internet */
+extern const char * ext_if_name;
-#ifdef ENABLE_NLS
-#define _(string) gettext(string)
-#else
-#define _(string) (string)
+/* file to store all leases */
+#ifdef ENABLE_LEASEFILE
+extern const char * lease_file;
#endif
-#ifndef PNPX
-#define PNPX 0
-#endif
+/* forced ip address to use for this interface
+ * when NULL, getifaddr() is used */
+extern const char * use_ext_ip_addr;
-#if 0 // Add these once the newer ffmpeg libs that can detect WMAPRO are more widely used
- "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
- "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0,"
-#endif
-#define RESOURCE_PROTOCOL_INFO_VALUES \
- "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," \
- "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HP_HD_AC3_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AC3;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AC3_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AC3;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AC3_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_HD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_CIF15_AAC_520;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_CIF30_AAC_940;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_L31_HD_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_L32_HD_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_L3L_SD_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_VGA_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_HD_NA;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_HD_NA_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPLL_BASE;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPML_BASE;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVSPML_MP3;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAPRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:audio/3gpp:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_MULT5_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
- "http-get:*:image/jpeg:*," \
- "http-get:*:video/avi:*," \
- "http-get:*:video/divx:*," \
- "http-get:*:video/x-matroska:*," \
- "http-get:*:video/mpeg:*," \
- "http-get:*:video/mp4:*," \
- "http-get:*:video/x-ms-wmv:*," \
- "http-get:*:video/x-msvideo:*," \
- "http-get:*:video/x-flv:*," \
- "http-get:*:video/x-tivo-mpeg:*," \
- "http-get:*:video/quicktime:*," \
- "http-get:*:audio/mp4:*," \
- "http-get:*:audio/x-wav:*," \
- "http-get:*:audio/x-flac:*," \
- "http-get:*:application/ogg:*"
+/* parameters to return to upnp client when asked */
+extern unsigned long downstream_bitrate;
+extern unsigned long upstream_bitrate;
/* statup time */
extern time_t startup_time;
-extern struct runtime_vars_s runtime_vars;
/* runtime boolean flags */
extern int runtime_flags;
-#define INOTIFY_MASK 0x0001
-#define TIVO_MASK 0x0002
-#define DLNA_STRICT_MASK 0x0004
+#define LOGPACKETSMASK 0x0001
+#define SYSUPTIMEMASK 0x0002
+#ifdef ENABLE_NATPMP
+#define ENABLENATPMPMASK 0x0004
+#endif
+#define CHECKCLIENTIPMASK 0x0008
+#define SECUREMODEMASK 0x0010
+
+#ifdef PF_ENABLE_FILTER_RULES
+#define PFNOQUICKRULESMASK 0x0040
+#endif
#define SETFLAG(mask) runtime_flags |= mask
#define GETFLAG(mask) runtime_flags & mask
@@ -184,20 +54,30 @@ extern const char * pidfilename;
extern char uuidvalue[];
-#define MODELNAME_MAX_LEN (64)
-extern char modelname[];
+#define SERIALNUMBER_MAX_LEN (10)
+extern char serialnumber[];
-#define MODELNUMBER_MAX_LEN (16)
+#define MODELNUMBER_MAX_LEN (48)
extern char modelnumber[];
-#define SERIALNUMBER_MAX_LEN (16)
-extern char serialnumber[];
-
#define PRESENTATIONURL_MAX_LEN (64)
extern char presentationurl[];
-#if PNPX
-extern char pnpx_hwid[];
+/* UPnP permission rules : */
+extern struct upnpperm * upnppermlist;
+extern unsigned int num_upnpperm;
+
+#ifdef ENABLE_NATPMP
+/* NAT-PMP */
+extern unsigned int nextnatpmptoclean_timestamp;
+extern unsigned short nextnatpmptoclean_eport;
+extern unsigned short nextnatpmptoclean_proto;
+#endif
+
+#ifdef USE_PF
+/* queue and tag for PF rules */
+extern const char * queue;
+extern const char * tag;
#endif
/* lan addresses */
@@ -209,16 +89,7 @@ extern struct lan_addr_s lan_addr[];
/* UPnP-A/V [DLNA] */
extern sqlite3 *db;
-extern char dlna_no_conv[];
-#define FRIENDLYNAME_MAX_LEN (64)
-extern char friendly_name[];
-extern char db_path[];
-extern char log_path[];
-extern struct media_dir_s * media_dirs;
-extern struct album_art_name_s * album_art_names;
-extern struct client_cache_s clients[CLIENT_CACHE_SLOTS];
-extern short int scanning;
-extern volatile short int quitting;
-extern volatile uint32_t updateID;
+#define MEDIADIR_MAX_LEN (256)
+extern char media_dir[];
#endif
diff --git a/upnphttp.c b/upnphttp.c
index 8495340..54432cf 100644
--- a/upnphttp.c
+++ b/upnphttp.c
@@ -1,51 +1,11 @@
-/* MiniDLNA project
- *
- * http://sourceforge.net/projects/minidlna/
- *
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- *
- * Portions of the code from the MiniUPnP project:
- *
- * Copyright (c) 2006-2007, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+/* $Id$ */
+/* Project : miniupnp
+ * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * Author : Thomas Bernard
+ * Copyright (c) 2005-2008 Thomas Bernard
+ * This software is subject to the conditions detailed in the
+ * LICENCE file included in this distribution.
+ * */
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
@@ -53,11 +13,12 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
+#include <syslog.h>
#include <ctype.h>
#include "config.h"
#include "upnphttp.h"
#include "upnpdescgen.h"
-#include "minidlnapath.h"
+#include "miniupnpdpath.h"
#include "upnpsoap.h"
#include "upnpevents.h"
@@ -66,24 +27,13 @@
#include <fcntl.h>
#include <errno.h>
#include <sys/sendfile.h>
-#include <arpa/inet.h>
#include "upnpglobalvars.h"
-#include "utils.h"
-#include "getifaddr.h"
-#include "image_utils.h"
-#include "log.h"
-#include "sql.h"
+#include <sqlite3.h>
#include <libexif/exif-loader.h>
-#ifdef TIVO_SUPPORT
-#include "tivo_utils.h"
-#include "tivo_commands.h"
+#if 0 //JPEG_RESIZE
+#include <gd.h>
#endif
-//#define MAX_BUFFER_SIZE 4194304 // 4MB -- Too much?
-#define MAX_BUFFER_SIZE 2147483647 // 2GB -- Too much?
-#define MIN_BUFFER_SIZE 65536
-
-#include "icons.c"
struct upnphttp *
New_upnphttp(int s)
@@ -104,7 +54,7 @@ CloseSocket_upnphttp(struct upnphttp * h)
{
if(close(h->socket) < 0)
{
- DPRINTF(E_ERROR, L_HTTP, "CloseSocket_upnphttp: close(%d): %s\n", h->socket, strerror(errno));
+ syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
}
h->socket = -1;
h->state = 100;
@@ -125,40 +75,6 @@ Delete_upnphttp(struct upnphttp * h)
}
}
-int
-SearchClientCache(struct in_addr addr, int quiet)
-{
- int i;
- for( i=0; i<CLIENT_CACHE_SLOTS; i++ )
- {
- if( clients[i].addr.s_addr == addr.s_addr )
- {
- /* Invalidate this client cache if it's older than 1 hour */
- if( (time(NULL) - clients[i].age) > 3600 )
- {
- unsigned char mac[6];
- if( get_remote_mac(addr, mac) == 0 &&
- memcmp(mac, clients[i].mac, 6) == 0 )
- {
- /* Same MAC as last time when we were able to identify the client,
- * so extend the timeout by another hour. */
- clients[i].age = time(NULL);
- }
- else
- {
- memset(&clients[i], 0, sizeof(struct client_cache_s));
- return -1;
- }
- }
- if( !quiet )
- DPRINTF(E_DEBUG, L_HTTP, "Client found in cache. [type %d/entry %d]\n",
- clients[i].type, i);
- return i;
- }
- }
- return -1;
-}
-
/* parse HttpHeaders of the REQUEST */
static void
ParseHttpHeaders(struct upnphttp * h)
@@ -180,6 +96,9 @@ ParseHttpHeaders(struct upnphttp * h)
while(*p < '0' || *p > '9')
p++;
h->req_contentlen = atoi(p);
+ /*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
+ printf(" readbufflen=%d contentoff = %d\n",
+ h->req_buflen, h->req_contentoff);*/
}
else if(strncasecmp(line, "SOAPAction", 10)==0)
{
@@ -199,6 +118,7 @@ ParseHttpHeaders(struct upnphttp * h)
h->req_soapAction = p;
h->req_soapActionLen = n;
}
+#ifdef ENABLE_EVENTS
else if(strncasecmp(line, "Callback", 8)==0)
{
p = colon;
@@ -212,26 +132,14 @@ ParseHttpHeaders(struct upnphttp * h)
}
else if(strncasecmp(line, "SID", 3)==0)
{
- //zqiu: fix bug for test 4.0.5
- //Skip extra headers like "SIDHEADER: xxxxxx xxx"
- for(p=line+3;p<colon;p++)
- {
- if(!isspace(*p))
- {
- p = NULL; //unexpected header
- break;
- }
- }
- if(p) {
- p = colon + 1;
- while(isspace(*p))
- p++;
- n = 0;
- while(!isspace(p[n]))
- n++;
- h->req_SID = p;
- h->req_SIDLen = n;
- }
+ p = colon + 1;
+ while(isspace(*p))
+ p++;
+ n = 0;
+ while(!isspace(p[n]))
+ n++;
+ h->req_SID = p;
+ h->req_SIDLen = n;
}
/* Timeout: Seconds-nnnn */
/* TIMEOUT
@@ -249,6 +157,8 @@ intervening space) by either an integer or the keyword "infinite". */
h->req_Timeout = atoi(p+7);
}
}
+#endif
+#if 1
// Range: bytes=xxx-yyy
else if(strncasecmp(line, "Range", 5)==0)
{
@@ -259,107 +169,13 @@ intervening space) by either an integer or the keyword "infinite". */
h->reqflags |= FLAG_RANGE;
h->req_RangeEnd = atoll(index(p+6, '-')+1);
h->req_RangeStart = atoll(p+6);
- DPRINTF(E_DEBUG, L_HTTP, "Range Start-End: %lld - %lld\n",
- h->req_RangeStart, h->req_RangeEnd?h->req_RangeEnd:-1);
+printf("Range Start-End: %lld-%lld\n", h->req_RangeStart, h->req_RangeEnd);
}
}
else if(strncasecmp(line, "Host", 4)==0)
{
h->reqflags |= FLAG_HOST;
}
- else if(strncasecmp(line, "User-Agent", 10)==0)
- {
- /* Skip client detection if we already detected it. */
- if( h->req_client )
- goto next_header;
- p = colon + 1;
- while(isspace(*p))
- p++;
- if(strncasecmp(p, "Xbox/", 5)==0)
- {
- h->req_client = EXbox;
- h->reqflags |= FLAG_MIME_AVI_AVI;
- h->reqflags |= FLAG_MS_PFS;
- }
- else if(strncmp(p, "PLAYSTATION", 11)==0)
- {
- h->req_client = EPS3;
- h->reqflags |= FLAG_DLNA;
- h->reqflags |= FLAG_MIME_AVI_DIVX;
- }
- else if(strncmp(p, "SamsungWiselinkPro", 18)==0 ||
- strncmp(p, "SEC_HHP_", 8)==0)
- {
- h->req_client = ESamsungTV;
- h->reqflags |= FLAG_DLNA;
- h->reqflags |= FLAG_NO_RESIZE;
- //h->reqflags |= FLAG_MIME_AVI_DIVX;
- }
- else if(strstrc(p, "bridgeCo-DMP/3", '\r'))
- {
- h->req_client = EDenonReceiver;
- h->reqflags |= FLAG_DLNA;
- }
- else if(strstrc(p, "fbxupnpav/", '\r'))
- {
- h->req_client = EFreeBox;
- }
- else if(strncmp(p, "SMP8634", 7)==0)
- {
- h->req_client = EPopcornHour;
- h->reqflags |= FLAG_MIME_FLAC_FLAC;
- }
- else if(strstrc(p, "Microsoft-IPTV-Client", '\r'))
- {
- h->req_client = EMediaRoom;
- h->reqflags |= FLAG_MS_PFS;
- }
- else if(strstrc(p, "UPnP/1.0 DLNADOC/1.50 Intel_SDK_for_UPnP_devices/1.2", '\r'))
- {
- h->req_client = EToshibaTV;
- h->reqflags |= FLAG_DLNA;
- }
- else if(strstrc(p, "DLNADOC/1.50", '\r'))
- {
- h->req_client = EStandardDLNA150;
- h->reqflags |= FLAG_DLNA;
- h->reqflags |= FLAG_MIME_AVI_AVI;
- }
- }
- else if(strncasecmp(line, "X-AV-Client-Info", 16)==0)
- {
- /* Skip client detection if we already detected it. */
- if( h->req_client && h->req_client < EStandardDLNA150 )
- goto next_header;
- p = colon + 1;
- while(isspace(*p))
- p++;
- if(strstrc(p, "PLAYSTATION 3", '\r'))
- {
- h->req_client = EPS3;
- h->reqflags |= FLAG_DLNA;
- h->reqflags |= FLAG_MIME_AVI_DIVX;
- }
- /* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Blu-ray Disc Player"; mv="2.0" */
- /* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="BLU-RAY HOME THEATRE SYSTEM"; mv="2.0"; */
- /* Sony SMP-100 needs the same treatment as their BDP-S370 */
- /* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="Media Player"; mv="2.0" */
- else if(strstrc(p, "Blu-ray Disc Player", '\r') ||
- strstrc(p, "BLU-RAY HOME THEATRE SYSTEM", '\r') ||
- strstrc(p, "Media Player", '\r'))
- {
- h->req_client = ESonyBDP;
- h->reqflags |= FLAG_DLNA;
- }
- /* X-AV-Client-Info: av=5.0; cn="Sony Corporation"; mn="BRAVIA KDL-40EX503"; mv="1.7"; */
- /* X-AV-Client-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="INTERNET TV NSX-40GT 1"; mv="0.1"; */
- else if(strstrc(p, "BRAVIA", '\r') ||
- strstrc(p, "INTERNET TV", '\r'))
- {
- h->req_client = ESonyBravia;
- h->reqflags |= FLAG_DLNA;
- }
- }
else if(strncasecmp(line, "Transfer-Encoding", 17)==0)
{
p = colon + 1;
@@ -375,17 +191,13 @@ intervening space) by either an integer or the keyword "infinite". */
p = colon + 1;
while(isspace(*p))
p++;
- if( (*p != '1') || !isspace(p[1]) )
+ if( strcmp(p, "1") != 0 )
h->reqflags |= FLAG_INVALID_REQ;
}
else if(strncasecmp(line, "TimeSeekRange.dlna.org", 22)==0)
{
h->reqflags |= FLAG_TIMESEEK;
}
- else if(strncasecmp(line, "PlaySpeed.dlna.org", 18)==0)
- {
- h->reqflags |= FLAG_PLAYSPEED;
- }
else if(strncasecmp(line, "realTimeInfo.dlna.org", 21)==0)
{
h->reqflags |= FLAG_REALTIMEINFO;
@@ -408,77 +220,29 @@ intervening space) by either an integer or the keyword "infinite". */
h->reqflags |= FLAG_XFERBACKGROUND;
}
}
- else if(strncasecmp(line, "getCaptionInfo.sec", 18)==0)
- {
- h->reqflags |= FLAG_CAPTION;
- }
+#endif
}
-next_header:
while(!(line[0] == '\r' && line[1] == '\n'))
line++;
line += 2;
}
if( h->reqflags & FLAG_CHUNKED )
{
- char *endptr;
- h->req_chunklen = -1;
- if( h->req_buflen <= h->req_contentoff )
- return;
- while( (line < (h->req_buf + h->req_buflen)) &&
- (h->req_chunklen = strtol(line, &endptr, 16)) &&
- (endptr != line) )
+ if( h->req_buflen > h->req_contentoff )
{
- while(!(endptr[0] == '\r' && endptr[1] == '\n'))
+ h->req_chunklen = strtol(line, NULL, 16);
+ while(!(line[0] == '\r' && line[1] == '\n'))
{
- endptr++;
+ line++;
+ h->req_contentoff++;
}
- line = endptr+h->req_chunklen+2;
+ h->req_contentoff += 2;
}
-
- if( endptr == line )
+ else
{
h->req_chunklen = -1;
- return;
}
}
- /* If the client type wasn't found, search the cache.
- * This is done because a lot of clients like to send a
- * different User-Agent with different types of requests. */
- n = SearchClientCache(h->clientaddr, 0);
- if( h->req_client )
- {
- /* Add this client to the cache if it's not there already. */
- if( n < 0 )
- {
- for( n=0; n<CLIENT_CACHE_SLOTS; n++ )
- {
- if( clients[n].addr.s_addr )
- continue;
- get_remote_mac(h->clientaddr, clients[n].mac);
- clients[n].addr = h->clientaddr;
- DPRINTF(E_DEBUG, L_HTTP, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
- h->req_client, inet_ntoa(clients[n].addr),
- clients[n].mac[0], clients[n].mac[1], clients[n].mac[2],
- clients[n].mac[3], clients[n].mac[4], clients[n].mac[5], n);
- break;
- }
- }
- else if( (clients[n].type < EStandardDLNA150) && (h->req_client == EStandardDLNA150) )
- {
- /* If we know the client and our new detection is generic, use our cached info */
- h->reqflags |= clients[n].flags;
- h->req_client = clients[n].type;
- return;
- }
- clients[n].type = h->req_client;
- clients[n].flags = h->reqflags & 0xFFF00000;
- clients[n].age = time(NULL);
- }
- else if( n >= 0 )
- {
- h->reqflags |= clients[n].flags;
- h->req_client = clients[n].type;
- }
}
/* very minimalistic 400 error message */
@@ -500,6 +264,20 @@ Send400(struct upnphttp * h)
static void
Send404(struct upnphttp * h)
{
+/*
+ static const char error404[] = "HTTP/1.1 404 Not found\r\n"
+ "Connection: close\r\n"
+ "Content-type: text/html\r\n"
+ "\r\n"
+ "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
+ "<BODY><H1>Not Found</H1>The requested URL was not found"
+ " on this server.</BODY></HTML>\r\n";
+ int n;
+ n = send(h->socket, error404, sizeof(error404) - 1, 0);
+ if(n < 0)
+ {
+ syslog(LOG_ERR, "Send404: send(http): %m");
+ }*/
static const char body404[] =
"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
"<BODY><H1>Not Found</H1>The requested URL was not found"
@@ -511,13 +289,13 @@ Send404(struct upnphttp * h)
CloseSocket_upnphttp(h);
}
-/* very minimalistic 406 error message */
+/* very minimalistic 404 error message */
static void
Send406(struct upnphttp * h)
{
static const char body406[] =
"<HTML><HEAD><TITLE>406 Not Acceptable</TITLE></HEAD>"
- "<BODY><H1>Not Acceptable</H1>An unsupported operation"
+ "<BODY><H1>Not Acceptable</H1>An unsupported operation "
" was requested.</BODY></HTML>\r\n";
h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 406, "Not Acceptable",
@@ -526,7 +304,7 @@ Send406(struct upnphttp * h)
CloseSocket_upnphttp(h);
}
-/* very minimalistic 416 error message */
+/* very minimalistic 404 error message */
static void
Send416(struct upnphttp * h)
{
@@ -541,25 +319,25 @@ Send416(struct upnphttp * h)
CloseSocket_upnphttp(h);
}
-/* very minimalistic 500 error message */
-void
-Send500(struct upnphttp * h)
-{
- static const char body500[] =
- "<HTML><HEAD><TITLE>500 Internal Server Error</TITLE></HEAD>"
- "<BODY><H1>Internal Server Error</H1>Server encountered "
- "and Internal Error.</BODY></HTML>\r\n";
- h->respflags = FLAG_HTML;
- BuildResp2_upnphttp(h, 500, "Internal Server Errror",
- body500, sizeof(body500) - 1);
- SendResp_upnphttp(h);
- CloseSocket_upnphttp(h);
-}
-
/* very minimalistic 501 error message */
-void
+static void
Send501(struct upnphttp * h)
{
+/*
+ static const char error501[] = "HTTP/1.1 501 Not Implemented\r\n"
+ "Connection: close\r\n"
+ "Content-type: text/html\r\n"
+ "\r\n"
+ "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
+ "<BODY><H1>Not Implemented</H1>The HTTP Method "
+ "is not implemented by this server.</BODY></HTML>\r\n";
+ int n;
+ n = send(h->socket, error501, sizeof(error501) - 1, 0);
+ if(n < 0)
+ {
+ syslog(LOG_ERR, "Send501: send(http): %m");
+ }
+*/
static const char body501[] =
"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
"<BODY><H1>Not Implemented</H1>The HTTP Method "
@@ -574,7 +352,7 @@ Send501(struct upnphttp * h)
static const char *
findendheaders(const char * s, int len)
{
- while(len-- > 0)
+ while(len-->0)
{
if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
return s;
@@ -583,6 +361,25 @@ findendheaders(const char * s, int len)
return NULL;
}
+#ifdef HAS_DUMMY_SERVICE
+static void
+sendDummyDesc(struct upnphttp * h)
+{
+ static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
+ "<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
+ " <specVersion>"
+ " <major>1</major>"
+ " <minor>0</minor>"
+ " </specVersion>"
+ " <actionList />"
+ " <serviceStateTable />"
+ "</scpd>\r\n";
+ BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
+ SendResp_upnphttp(h);
+ CloseSocket_upnphttp(h);
+}
+#endif
+
/* Sends the description generated by the parameter */
static void
sendXMLdesc(struct upnphttp * h, char * (f)(int *))
@@ -592,11 +389,17 @@ sendXMLdesc(struct upnphttp * h, char * (f)(int *))
desc = f(&len);
if(!desc)
{
- DPRINTF(E_ERROR, L_HTTP, "Failed to generate XML description\n");
- Send500(h);
- return;
+ static const char error500[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
+ "</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
+ syslog(LOG_ERR, "Failed to generate XML description");
+ h->respflags = FLAG_HTML;
+ BuildResp2_upnphttp(h, 500, "Internal Server Error",
+ error500, sizeof(error500)-1);
+ }
+ else
+ {
+ BuildResp_upnphttp(h, desc, len);
}
- BuildResp_upnphttp(h, desc, len);
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
free(desc);
@@ -612,7 +415,10 @@ ProcessHTTPPOST_upnphttp(struct upnphttp * h)
if(h->req_soapAction)
{
/* we can process the request */
- DPRINTF(E_DEBUG, L_HTTP, "SOAPAction: %.*s\n", h->req_soapActionLen, h->req_soapAction);
+//printf("__LINE %d__ SOAPAction: %s [%d]\n", __LINE__, h->req_soapAction, h->req_soapActionLen);
+// syslog(LOG_INFO, "SOAPAction: %.*s",
+// h->req_soapActionLen, h->req_soapAction);
+//printf("__LINE %d__ SOAPAction: %.*s\n", __LINE__, h->req_soapActionLen, h->req_soapAction);
ExecuteSoapAction(h,
h->req_soapAction,
h->req_soapActionLen);
@@ -621,7 +427,7 @@ ProcessHTTPPOST_upnphttp(struct upnphttp * h)
{
static const char err400str[] =
"<html><body>Bad request</body></html>";
- DPRINTF(E_WARN, L_HTTP, "No SOAPAction in HTTP headers\n");
+ syslog(LOG_INFO, "No SOAPAction in HTTP headers");
h->respflags = FLAG_HTML;
BuildResp2_upnphttp(h, 400, "Bad Request",
err400str, sizeof(err400str) - 1);
@@ -636,14 +442,15 @@ ProcessHTTPPOST_upnphttp(struct upnphttp * h)
}
}
+#ifdef ENABLE_EVENTS
static void
ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
{
const char * sid;
- DPRINTF(E_DEBUG, L_HTTP, "ProcessHTTPSubscribe %s\n", path);
- DPRINTF(E_DEBUG, L_HTTP, "Callback '%.*s' Timeout=%d\n",
+ syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path);
+ syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d",
h->req_CallbackLen, h->req_Callback, h->req_Timeout);
- DPRINTF(E_DEBUG, L_HTTP, "SID '%.*s'\n", h->req_SIDLen, h->req_SID);
+ syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_SID);
if(!h->req_Callback && !h->req_SID) {
/* Missing or invalid CALLBACK : 412 Precondition Failed.
* If CALLBACK header is missing or does not contain a valid HTTP URL,
@@ -661,7 +468,7 @@ ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
h->req_CallbackLen, h->req_Timeout);
h->respflags = FLAG_TIMEOUT;
if(sid) {
- DPRINTF(E_DEBUG, L_HTTP, "generated sid=%s\n", sid);
+ syslog(LOG_DEBUG, "generated sid=%s", sid);
h->respflags |= FLAG_SID;
h->req_SID = sid;
h->req_SIDLen = strlen(sid);
@@ -679,7 +486,6 @@ with HTTP error 412 Precondition Failed. */
/* A DLNA device must enforce a 5 minute timeout */
h->respflags = FLAG_TIMEOUT;
h->req_Timeout = 300;
- h->respflags |= FLAG_SID;
BuildResp_upnphttp(h, 0, 0);
}
}
@@ -691,8 +497,8 @@ with HTTP error 412 Precondition Failed. */
static void
ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
{
- DPRINTF(E_DEBUG, L_HTTP, "ProcessHTTPUnSubscribe %s\n", path);
- DPRINTF(E_DEBUG, L_HTTP, "SID '%.*s'\n", h->req_SIDLen, h->req_SID);
+ syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
+ syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_SID);
/* Remove from the list */
if(upnpevents_removeSubscriber(h->req_SID, h->req_SIDLen) < 0) {
BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
@@ -702,6 +508,7 @@ ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
SendResp_upnphttp(h);
CloseSocket_upnphttp(h);
}
+#endif
/* Parse and process Http Query
* called once all the HTTP headers have been received. */
@@ -709,7 +516,7 @@ static void
ProcessHttpQuery_upnphttp(struct upnphttp * h)
{
char HttpCommand[16];
- char HttpUrl[512];
+ char HttpUrl[128];
char * HttpVer;
char * p;
int i;
@@ -727,7 +534,7 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
while(*p!='/')
p++;
}
- for(i = 0; i<511 && *p != ' ' && *p != '\r'; i++)
+ for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
HttpUrl[i] = *(p++);
HttpUrl[i] = '\0';
while(*p==' ')
@@ -736,41 +543,18 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
for(i = 0; i<15 && *p != '\r'; i++)
HttpVer[i] = *(p++);
HttpVer[i] = '\0';
- /*DPRINTF(E_INFO, L_HTTP, "HTTP REQUEST : %s %s (%s)\n",
- HttpCommand, HttpUrl, HttpVer);*/
+ syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
+ HttpCommand, HttpUrl, HttpVer);
ParseHttpHeaders(h);
- /* see if we need to wait for remaining data */
- if( (h->reqflags & FLAG_CHUNKED) )
+ if( (h->reqflags & FLAG_CHUNKED) && (h->req_chunklen > (h->req_buflen - h->req_contentoff) || h->req_chunklen < 0) )
{
- if( h->req_chunklen )
- {
- h->state = 2;
- return;
- }
- char *chunkstart, *chunk, *endptr, *endbuf;
- chunk = endbuf = chunkstart = h->req_buf + h->req_contentoff;
-
- while( (h->req_chunklen = strtol(chunk, &endptr, 16)) && (endptr != chunk) )
- {
- while(!(endptr[0] == '\r' && endptr[1] == '\n'))
- {
- endptr++;
- }
- endptr += 2;
-
- memmove(endbuf, endptr, h->req_chunklen);
-
- endbuf += h->req_chunklen;
- chunk = endptr + h->req_chunklen;
- }
- h->req_contentlen = endbuf - chunkstart;
- h->req_buflen = endbuf - h->req_buf;
- h->state = 100;
+ /* waiting for remaining data */
+ printf("*** %d < %d\n", (h->req_buflen - h->req_contentoff), h->req_contentlen);
+ printf("Chunked request [%ld]. Need more input.\n", h->req_chunklen);
+ h->state = 2;
}
-
- DPRINTF(E_DEBUG, L_HTTP, "HTTP REQUEST: %.*s\n", h->req_buflen, h->req_buf);
- if(strcmp("POST", HttpCommand) == 0)
+ else if(strcmp("POST", HttpCommand) == 0)
{
h->req_command = EPost;
ProcessHTTPPOST_upnphttp(h);
@@ -779,20 +563,14 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
{
if( ((strcmp(h->HttpVer, "HTTP/1.1")==0) && !(h->reqflags & FLAG_HOST)) || (h->reqflags & FLAG_INVALID_REQ) )
{
- DPRINTF(E_WARN, L_HTTP, "Invalid request, responding ERROR 400. (No Host specified in HTTP headers?)\n");
+ syslog(LOG_NOTICE, "No Host specified in HTTP headers, responding ERROR 400");
Send400(h);
- return;
}
- #if 1 /* 7.3.33.4 */
- else if( ((h->reqflags & FLAG_TIMESEEK) || (h->reqflags & FLAG_PLAYSPEED)) &&
- !(h->reqflags & FLAG_RANGE) )
+ else if( h->reqflags & FLAG_TIMESEEK )
{
- DPRINTF(E_WARN, L_HTTP, "DLNA %s requested, responding ERROR 406\n",
- h->reqflags&FLAG_TIMESEEK ? "TimeSeek" : "PlaySpeed");
+ syslog(LOG_NOTICE, "DLNA TimeSeek requested, responding ERROR 406");
Send406(h);
- return;
}
- #endif
else if(strcmp("GET", HttpCommand) == 0)
{
h->req_command = EGet;
@@ -803,18 +581,7 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
}
if(strcmp(ROOTDESC_PATH, HttpUrl) == 0)
{
- /* If it's a Xbox360, we might need a special friendly_name to be recognized */
- if( (h->req_client == EXbox) && !strchr(friendly_name, ':') )
- {
- i = strlen(friendly_name);
- snprintf(friendly_name+i, FRIENDLYNAME_MAX_LEN-i, ": 1");
- sendXMLdesc(h, genRootDesc);
- friendly_name[i] = '\0';
- }
- else
- {
- sendXMLdesc(h, genRootDesc);
- }
+ sendXMLdesc(h, genRootDesc);
}
else if(strcmp(CONTENTDIRECTORY_PATH, HttpUrl) == 0)
{
@@ -828,6 +595,12 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
{
sendXMLdesc(h, genX_MS_MediaReceiverRegistrar);
}
+#ifdef HAS_DUMMY_SERVICE
+ else if(strcmp(DUMMY_PATH, HttpUrl) == 0)
+ {
+ sendDummyDesc(h);
+ }
+#endif
else if(strncmp(HttpUrl, "/MediaItems/", 12) == 0)
{
SendResp_dlnafile(h, HttpUrl+12);
@@ -836,56 +609,22 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
else if(strncmp(HttpUrl, "/Thumbnails/", 12) == 0)
{
SendResp_thumbnail(h, HttpUrl+12);
- }
- else if(strncmp(HttpUrl, "/AlbumArt/", 10) == 0)
- {
- SendResp_albumArt(h, HttpUrl+10);
CloseSocket_upnphttp(h);
}
- #ifdef TIVO_SUPPORT
- else if(strncmp(HttpUrl, "/TiVoConnect", 12) == 0)
- {
- if( GETFLAG(TIVO_MASK) )
- {
- if( *(HttpUrl+12) == '?' )
- {
- ProcessTiVoCommand(h, HttpUrl+13);
- }
- else
- {
- DPRINTF(E_WARN, L_HTTP, "Invalid TiVo request! %s\n", HttpUrl+12);
- Send404(h);
- }
- }
- else
- {
- DPRINTF(E_WARN, L_HTTP, "TiVo request with out TiVo support enabled! %s\n",
- HttpUrl+12);
- Send404(h);
- }
- }
- #endif
- else if(strncmp(HttpUrl, "/Resized/", 9) == 0)
+#if 0 //JPEG_RESIZE
+ else if(strncmp(HttpUrl, "/Resized/", 7) == 0)
{
- SendResp_resizedimg(h, HttpUrl+9);
- CloseSocket_upnphttp(h);
- }
- else if(strncmp(HttpUrl, "/icons/", 7) == 0)
- {
- SendResp_icon(h, HttpUrl+7);
- CloseSocket_upnphttp(h);
- }
- else if(strncmp(HttpUrl, "/Captions/", 10) == 0)
- {
- SendResp_caption(h, HttpUrl+10);
+ SendResp_resizedimg(h, HttpUrl+7);
CloseSocket_upnphttp(h);
}
+#endif
else
{
- DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", HttpUrl);
+ syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
Send404(h);
}
}
+#ifdef ENABLE_EVENTS
else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
{
h->req_command = ESubscribe;
@@ -896,9 +635,16 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
h->req_command = EUnSubscribe;
ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
}
+#else
+ else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
+ {
+ syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
+ Send501(h);
+ }
+#endif
else
{
- DPRINTF(E_WARN, L_HTTP, "Unsupported HTTP Command %s\n", HttpCommand);
+ syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
Send501(h);
}
}
@@ -917,12 +663,12 @@ Process_upnphttp(struct upnphttp * h)
n = recv(h->socket, buf, 2048, 0);
if(n<0)
{
- DPRINTF(E_ERROR, L_HTTP, "recv (state0): %s\n", strerror(errno));
+ syslog(LOG_ERR, "recv (state0): %m");
h->state = 100;
}
else if(n==0)
{
- DPRINTF(E_WARN, L_HTTP, "HTTP Connection closed unexpectedly\n");
+ syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
h->state = 100;
}
else
@@ -939,7 +685,6 @@ Process_upnphttp(struct upnphttp * h)
if(endheaders)
{
h->req_contentoff = endheaders - h->req_buf + 4;
- h->req_contentlen = h->req_buflen - h->req_contentoff;
ProcessHttpQuery_upnphttp(h);
}
}
@@ -949,12 +694,12 @@ Process_upnphttp(struct upnphttp * h)
n = recv(h->socket, buf, 2048, 0);
if(n<0)
{
- DPRINTF(E_ERROR, L_HTTP, "recv (state%d): %s\n", h->state, strerror(errno));
+ syslog(LOG_ERR, "recv (state1): %m");
h->state = 100;
}
else if(n==0)
{
- DPRINTF(E_WARN, L_HTTP, "HTTP Connection closed unexpectedly\n");
+ syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
h->state = 100;
}
else
@@ -965,31 +710,28 @@ Process_upnphttp(struct upnphttp * h)
h->req_buflen += n;
if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
{
- /* Need the struct to point to the realloc'd memory locations */
if( h->state == 1 )
- {
- ParseHttpHeaders(h);
ProcessHTTPPOST_upnphttp(h);
- }
else if( h->state == 2 )
- {
ProcessHttpQuery_upnphttp(h);
- }
}
}
break;
default:
- DPRINTF(E_WARN, L_HTTP, "Unexpected state: %d\n", h->state);
+ syslog(LOG_WARNING, "Unexpected state: %d", h->state);
}
}
static const char httpresphead[] =
"%s %d %s\r\n"
+ /*"Content-Type: text/xml; charset=\"utf-8\"\r\n"*/
"Content-Type: %s\r\n"
"Connection: close\r\n"
"Content-Length: %d\r\n"
- "Server: " MINIDLNA_SERVER_STRING "\r\n"
+ /*"Server: miniupnpd/1.0 UPnP/1.0\r\n"*/
// "Accept-Ranges: bytes\r\n"
+// "DATE: Wed, 24 Sep 2008 05:57:19 GMT\r\n"
+ //"Server: " MINIUPNPD_SERVER_STRING "\r\n"
; /*"\r\n";*/
/*
"<?xml version=\"1.0\"?>\n"
@@ -1011,7 +753,7 @@ BuildHeader_upnphttp(struct upnphttp * h, int respcode,
int templen;
if(!h->res_buf)
{
- templen = sizeof(httpresphead) + 192 + bodylen;
+ templen = sizeof(httpresphead) + 128 + bodylen;
h->res_buf = (char *)malloc(templen);
h->res_buf_alloclen = templen;
}
@@ -1022,6 +764,7 @@ BuildHeader_upnphttp(struct upnphttp * h, int respcode,
(h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
bodylen);
/* Additional headers */
+#ifdef ENABLE_EVENTS
if(h->respflags & FLAG_TIMEOUT) {
h->res_buflen += snprintf(h->res_buf + h->res_buflen,
h->res_buf_alloclen - h->res_buflen,
@@ -1040,21 +783,25 @@ BuildHeader_upnphttp(struct upnphttp * h, int respcode,
if(h->respflags & FLAG_SID) {
h->res_buflen += snprintf(h->res_buf + h->res_buflen,
h->res_buf_alloclen - h->res_buflen,
- "SID: %.*s\r\n", h->req_SIDLen, h->req_SID);
+ "SID: %s\r\n", h->req_SID);
}
+#endif
#if 0 // DLNA
+ h->res_buflen += snprintf(h->res_buf + h->res_buflen,
+ h->res_buf_alloclen - h->res_buflen,
+ "Server: Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0\r\n");
char szTime[30];
time_t curtime = time(NULL);
strftime(szTime, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
h->res_buflen += snprintf(h->res_buf + h->res_buflen,
h->res_buf_alloclen - h->res_buflen,
"Date: %s\r\n", szTime);
- h->res_buflen += snprintf(h->res_buf + h->res_buflen,
- h->res_buf_alloclen - h->res_buflen,
- "contentFeatures.dlna.org: \r\n");
- h->res_buflen += snprintf(h->res_buf + h->res_buflen,
- h->res_buf_alloclen - h->res_buflen,
- "EXT:\r\n");
+// h->res_buflen += snprintf(h->res_buf + h->res_buflen,
+// h->res_buf_alloclen - h->res_buflen,
+// "contentFeatures.dlna.org: \r\n");
+// h->res_buflen += snprintf(h->res_buf + h->res_buflen,
+// h->res_buf_alloclen - h->res_buflen,
+// "EXT:\r\n");
#endif
h->res_buf[h->res_buflen++] = '\r';
h->res_buf[h->res_buflen++] = '\n';
@@ -1090,602 +837,222 @@ void
SendResp_upnphttp(struct upnphttp * h)
{
int n;
- DPRINTF(E_DEBUG, L_HTTP, "HTTP RESPONSE: %.*s\n", h->res_buflen, h->res_buf);
+printf("HTTP RESPONSE:\n%.*s\n", h->res_buflen, h->res_buf);
n = send(h->socket, h->res_buf, h->res_buflen, 0);
if(n<0)
{
- DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %s\n", strerror(errno));
- }
- else if(n < h->res_buflen)
- {
- /* TODO : handle correctly this case */
- DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %d bytes sent (out of %d)\n",
- n, h->res_buflen);
+ syslog(LOG_ERR, "send(res_buf): %m");
}
-}
-
-int
-send_data(struct upnphttp * h, char * header, size_t size, int flags)
-{
- int n;
-
- n = send(h->socket, header, size, flags);
- if(n<0)
- {
- DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %s\n", strerror(errno));
- }
else if(n < h->res_buflen)
{
/* TODO : handle correctly this case */
- DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %d bytes sent (out of %d)\n",
+ syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
n, h->res_buflen);
}
- else
- {
- return 0;
- }
- return 1;
}
void
-send_file(struct upnphttp * h, int sendfd, off_t offset, off_t end_offset)
-{
- off_t send_size;
- off_t ret;
- char *buf = NULL;
- int try_sendfile = 1;
-
- while( offset < end_offset )
- {
- if( try_sendfile )
- {
- send_size = ( ((end_offset - offset) < MAX_BUFFER_SIZE) ? (end_offset - offset + 1) : MAX_BUFFER_SIZE);
- ret = sendfile(h->socket, sendfd, &offset, send_size);
- if( ret == -1 )
- {
- DPRINTF(E_DEBUG, L_HTTP, "sendfile error :: error no. %d [%s]\n", errno, strerror(errno));
- /* If sendfile isn't supported on the filesystem, don't bother trying to use it again. */
- if( errno == EOVERFLOW || errno == EINVAL )
- try_sendfile = 0;
- else if( errno != EAGAIN )
- break;
- }
- else
- {
- //DPRINTF(E_DEBUG, L_HTTP, "sent %lld bytes to %d. offset is now %lld.\n", ret, h->socket, offset);
- continue;
- }
- }
- /* Fall back to regular I/O */
- if( !buf )
- buf = malloc(MIN_BUFFER_SIZE);
- send_size = ( ((end_offset - offset) < MIN_BUFFER_SIZE) ? (end_offset - offset + 1) : MIN_BUFFER_SIZE);
- lseek(sendfd, offset, SEEK_SET);
- ret = read(sendfd, buf, send_size);
- if( ret == -1 ) {
- DPRINTF(E_DEBUG, L_HTTP, "read error :: error no. %d [%s]\n", errno, strerror(errno));
- if( errno != EAGAIN )
- break;
- }
- ret = write(h->socket, buf, ret);
- if( ret == -1 ) {
- DPRINTF(E_DEBUG, L_HTTP, "write error :: error no. %d [%s]\n", errno, strerror(errno));
- if( errno != EAGAIN )
- break;
- }
- offset+=ret;
- }
- if( buf )
- free(buf);
-}
-
-void
-SendResp_icon(struct upnphttp * h, char * icon)
-{
- char * header;
- char * data;
- int size, ret;
- char mime[12];
- char date[30];
- time_t curtime = time(NULL);
-
- if( strcmp(icon, "sm.png") == 0 )
- {
- DPRINTF(E_DEBUG, L_HTTP, "Sending small PNG icon\n");
- data = (char *)png_sm;
- size = sizeof(png_sm)-1;
- strcpy(mime, "image/png");
- }
- else if( strcmp(icon, "lrg.png") == 0 )
- {
- DPRINTF(E_DEBUG, L_HTTP, "Sending large PNG icon\n");
- data = (char *)png_lrg;
- size = sizeof(png_lrg)-1;
- strcpy(mime, "image/png");
- }
- else if( strcmp(icon, "sm.jpg") == 0 )
- {
- DPRINTF(E_DEBUG, L_HTTP, "Sending small JPEG icon\n");
- data = (char *)jpeg_sm;
- size = sizeof(jpeg_sm)-1;
- strcpy(mime, "image/jpeg");
- }
- else if( strcmp(icon, "lrg.jpg") == 0 )
- {
- DPRINTF(E_DEBUG, L_HTTP, "Sending large JPEG icon\n");
- data = (char *)jpeg_lrg;
- size = sizeof(jpeg_lrg)-1;
- strcpy(mime, "image/jpeg");
- }
- else
- {
- DPRINTF(E_WARN, L_HTTP, "Invalid icon request: %s\n", icon);
- Send404(h);
- return;
- }
-
-
- strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
- ret = asprintf(&header, "HTTP/1.1 200 OK\r\n"
- "Content-Type: %s\r\n"
- "Content-Length: %d\r\n"
- "Connection: close\r\n"
- "Date: %s\r\n"
- "Server: " MINIDLNA_SERVER_STRING "\r\n\r\n",
- mime, size, date);
-
- if( (send_data(h, header, ret, MSG_MORE) == 0) && (h->req_command != EHead) )
- {
- send_data(h, data, size, 0);
- }
- free(header);
-}
-
-void
-SendResp_albumArt(struct upnphttp * h, char * object)
+SendResp_thumbnail(struct upnphttp * h, char * object)
{
char header[1500];
char sql_buf[256];
char **result;
- int rows = 0;
- char *path;
- char *dash;
char date[30];
time_t curtime = time(NULL);
- off_t offset = 0, size;
- int sendfh;
+ int n;
+ ExifData *ed;
+ ExifLoader *l;
memset(header, 0, 1500);
if( h->reqflags & FLAG_XFERSTREAMING || h->reqflags & FLAG_RANGE )
{
- DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
+ syslog(LOG_NOTICE, "Hey, you can't specify transferMode as Streaming with an image!");
Send406(h);
return;
}
- dash = strchr(object, '-');
- if( dash )
- *dash = '\0';
- sprintf(sql_buf, "SELECT PATH from ALBUM_ART where ID = %s", object);
- sql_get_table(db, sql_buf, &result, &rows, NULL);
- if( !rows )
- {
- DPRINTF(E_WARN, L_HTTP, "ALBUM_ART ID %s not found, responding ERROR 404\n", object);
- Send404(h);
- goto error;
- }
- path = result[1];
- DPRINTF(E_INFO, L_HTTP, "Serving album art ID: %s [%s]\n", object, path);
+ sprintf(sql_buf, "SELECT PATH from OBJECTS where OBJECT_ID = '%s'", object);
+ sqlite3_get_table(db, sql_buf, &result, 0, 0, 0);
+ printf("Serving up thumbnail for ObjectId: %s [%s]\n", object, result[1]);
- if( access(path, F_OK) == 0 )
+ if( access(result[1], F_OK) == 0 )
{
strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
- sendfh = open(path, O_RDONLY);
- if( sendfh < 0 ) {
- DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path);
+ l = exif_loader_new();
+ exif_loader_write_file(l, result[1]);
+ ed = exif_loader_get_data(l);
+ exif_loader_unref(l);
+
+ if( !ed->size )
+ {
+ Send404(h);
goto error;
}
- size = lseek(sendfh, 0, SEEK_END);
- lseek(sendfh, 0, SEEK_SET);
-
sprintf(header, "HTTP/1.1 200 OK\r\n"
"Content-Type: image/jpeg\r\n"
- "Content-Length: %jd\r\n"
+ "Content-Length: %d\r\n"
"Connection: close\r\n"
"Date: %s\r\n"
"EXT:\r\n"
- "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
- "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n"
- "Server: " MINIDLNA_SERVER_STRING "\r\n",
- size, date);
+ "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n"
+ "Server: RAIDiator/4.1, UPnP/1.0, MiniDLNA_TN/1.0\r\n",
+ ed->size, date);
if( h->reqflags & FLAG_XFERBACKGROUND )
{
- strcat(header, "transferMode.dlna.org: Background\r\n\r\n");
+ strcat(header, "transferMode.dlna.org: Background\r\n");
}
else //if( h->reqflags & FLAG_XFERINTERACTIVE )
{
- strcat(header, "transferMode.dlna.org: Interactive\r\n\r\n");
+ strcat(header, "transferMode.dlna.org: Interactive\r\n");
}
+ strcat(header, "\r\n");
-
- if( (send_data(h, header, strlen(header), MSG_MORE) == 0) && (h->req_command != EHead) && (sendfh > 0) )
+ n = send(h->socket, header, strlen(header), 0);
+ if(n<0)
{
- send_file(h, sendfh, offset, size);
+ syslog(LOG_ERR, "send(res_buf): %m");
+ }
+ else if(n < h->res_buflen)
+ {
+ /* TODO : handle correctly this case */
+ syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
+ n, h->res_buflen);
}
- close(sendfh);
- }
- error:
- sqlite3_free_table(result);
-}
-void
-SendResp_caption(struct upnphttp * h, char * object)
-{
- char header[1500];
- char sql_buf[256];
- char **result;
- int rows = 0;
- char *path;
- char date[30];
- time_t curtime = time(NULL);
- off_t offset = 0, size;
- int sendfh, ret;
-
- memset(header, 0, 1500);
-
- strip_ext(object);
- sprintf(sql_buf, "SELECT PATH from CAPTIONS where ID = %s", object);
- sql_get_table(db, sql_buf, &result, &rows, NULL);
- if( !rows )
- {
- DPRINTF(E_WARN, L_HTTP, "CAPTION ID %s not found, responding ERROR 404\n", object);
- Send404(h);
- goto error;
- }
- path = result[1];
- DPRINTF(E_INFO, L_HTTP, "Serving caption ID: %s [%s]\n", object, path);
-
- if( access(path, F_OK) != 0 )
- goto error;
-
- strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
- sendfh = open(path, O_RDONLY);
- if( sendfh < 0 ) {
- DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path);
- goto error;
- }
- size = lseek(sendfh, 0, SEEK_END);
- lseek(sendfh, 0, SEEK_SET);
-
- ret = snprintf(header, sizeof(header), "HTTP/1.1 200 OK\r\n"
- "Content-Type: smi/caption\r\n"
- "Content-Length: %jd\r\n"
- "Connection: close\r\n"
- "Date: %s\r\n"
- "EXT:\r\n"
- "Server: " MINIDLNA_SERVER_STRING "\r\n\r\n",
- size, date);
+ if( h->req_command == EHead )
+ {
+ exif_data_unref(ed);
+ goto error;
+ }
- if( (send_data(h, header, ret, MSG_MORE) == 0) && (h->req_command != EHead) && (sendfh > 0) )
- {
- send_file(h, sendfh, offset, size);
+ n = send(h->socket, ed->data, ed->size, 0);
+ if(n<0)
+ {
+ syslog(LOG_ERR, "send(res_buf): %m");
+ }
+ else if(n < h->res_buflen)
+ {
+ /* TODO : handle correctly this case */
+ syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
+ n, h->res_buflen);
+ }
+ exif_data_unref(ed);
}
- close(sendfh);
-
error:
sqlite3_free_table(result);
}
+#if 0 //JPEG_RESIZE
void
-SendResp_thumbnail(struct upnphttp * h, char * object)
+SendResp_resizedimg(struct upnphttp * h, char * object)
{
char header[1500];
char sql_buf[256];
char **result;
- int rows = 0;
- char *path;
char date[30];
time_t curtime = time(NULL);
- ExifData *ed;
- ExifLoader *l;
+ int n;
+ FILE *imgfile;
+ gdImagePtr imsrc = 0, imdst = 0;
+ int dstw, dsth, srcw, srch, size;
+ char * data;
memset(header, 0, 1500);
if( h->reqflags & FLAG_XFERSTREAMING || h->reqflags & FLAG_RANGE )
{
- DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
+ syslog(LOG_NOTICE, "You can't specify transferMode as Streaming with a resized image!");
Send406(h);
return;
}
- strip_ext(object);
- sprintf(sql_buf, "SELECT PATH from DETAILS where ID = '%s'", object);
- sql_get_table(db, sql_buf, &result, &rows, NULL);
- if( !rows )
- {
- DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
- Send404(h);
- goto error;
- }
- path = result[1];
- DPRINTF(E_INFO, L_HTTP, "Serving thumbnail for ObjectId: %s [%s]\n", object, path);
+ sprintf(sql_buf, "SELECT o.PATH, d.WIDTH, d.HEIGHT from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
+ sqlite3_get_table(db, sql_buf, &result, 0, 0, 0);
+ printf("Serving up resized image for ObjectId: %s [%s]\n", object, result[1]);
- if( access(path, F_OK) == 0 )
+ if( access(result[3], F_OK) == 0 )
{
strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
- l = exif_loader_new();
- exif_loader_write_file(l, path);
- ed = exif_loader_get_data(l);
- exif_loader_unref(l);
+ imgfile = fopen(result[3], "r");
+ imsrc = gdImageCreateFromJpeg(imgfile);
+ imdst = gdImageCreateTrueColor(dstw, dsth);
+ srcw = atoi(result[4]);
+ srch = atoi(result[5]);
+ dstw = 640;
+ dsth = ((((640<<10)/srcw)*srch)>>10);
- if( !ed || !ed->size )
+ if( !imsrc )
{
Send404(h);
- if( ed )
- exif_data_unref(ed);
goto error;
}
- sprintf(header, "HTTP/1.1 200 OK\r\n"
+ if( dsth > 480 )
+ {
+ dsth = 480;
+ dstw = (((480<<10)/srch) * srcw>>10);
+ }
+ gdImageCopyResized(imdst, imsrc, 0, 0, 0, 0, dstw, dsth, srcw, srch);
+ data = (char *)gdImageJpegPtr(imdst, &size, -1);
+ sprintf(header, "%s 200 OK\r\n"
"Content-Type: image/jpeg\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n"
"Date: %s\r\n"
"EXT:\r\n"
- "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
"contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n"
- "Server: " MINIDLNA_SERVER_STRING "\r\n",
- ed->size, date);
+ "Server: RAIDiator/4.1, UPnP/1.0, MiniDLNA_TN/1.0\r\n",
+ h->HttpVer, size, date);
- if( h->reqflags & FLAG_XFERBACKGROUND )
+ if( h->reqflags & FLAG_XFERINTERACTIVE )
{
- strcat(header, "transferMode.dlna.org: Background\r\n\r\n");
+ strcat(header, "transferMode.dlna.org: Interactive\r\n");
}
- else //if( h->reqflags & FLAG_XFERINTERACTIVE )
+ else if( h->reqflags & FLAG_XFERBACKGROUND )
{
- strcat(header, "transferMode.dlna.org: Interactive\r\n\r\n");
+ strcat(header, "transferMode.dlna.org: Background\r\n");
}
+ strcat(header, "\r\n");
- if( (send_data(h, header, strlen(header), MSG_MORE) == 0) && (h->req_command != EHead) )
- {
- send_data(h, (char *)ed->data, ed->size, 0);
- }
- exif_data_unref(ed);
- }
- CloseSocket_upnphttp(h);
- error:
- sqlite3_free_table(result);
-}
-
-void
-SendResp_resizedimg(struct upnphttp * h, char * object)
-{
- char header[1500];
- char str_buf[256];
- char **result;
- char date[30];
- char dlna_pn[4];
- time_t curtime = time(NULL);
- int width=640, height=480, dstw, dsth, size;
- long srcw, srch;
- unsigned char * data = NULL;
- char *path, *file_path;
- char *resolution;
- char *key, *val;
- char *saveptr=NULL, *item=NULL;
- /* Not implemented yet *
- char *pixelshape=NULL;
- int rotation; */
- sqlite_int64 id;
- int rows=0, chunked=0, ret;
-#ifdef __sparc__
- char *tn;
- ExifData *ed;
- ExifLoader *l;
-#endif
- image_s *imsrc = NULL, *imdst = NULL;
- int scale = 1;
-
- id = strtoll(object, NULL, 10);
- sprintf(str_buf, "SELECT PATH, RESOLUTION, THUMBNAIL from DETAILS where ID = '%lld'", id);
- ret = sql_get_table(db, str_buf, &result, &rows, NULL);
- if( (ret != SQLITE_OK) )
- {
- DPRINTF(E_ERROR, L_HTTP, "Didn't find valid file for %lld!\n", id);
- Send500(h);
- return;
- }
- if( !rows || (access(result[3], F_OK) != 0) )
- {
- DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
- sqlite3_free_table(result);
- Send404(h);
- return;
- }
-#if USE_FORK
- pid_t newpid = 0;
- newpid = fork();
- if( newpid )
- goto resized_error;
-#endif
- file_path = result[3];
- resolution = result[4];
- srcw = strtol(resolution, &saveptr, 10);
- srch = strtol(saveptr+1, NULL, 10);
-
- path = strdup(object);
- if( strtok_r(path, "?", &saveptr) )
- {
- item = strtok_r(NULL, "&,", &saveptr);
- }
- while( item != NULL )
- {
- #ifdef TIVO_SUPPORT
- decodeString(item, 1);
- #endif
- val = item;
- key = strsep(&val, "=");
- DPRINTF(E_DEBUG, L_GENERAL, "%s: %s\n", key, val);
- if( strcasecmp(key, "width") == 0 )
- {
- width = atoi(val);
- }
- else if( strcasecmp(key, "height") == 0 )
+ n = send(h->socket, header, strlen(header), 0);
+ if(n<0)
{
- height = atoi(val);
- }
- /* Not implemented yet *
- else if( strcasecmp(key, "rotation") == 0 )
+ syslog(LOG_ERR, "send(res_buf): %m");
+ }
+ else if(n < h->res_buflen)
{
- rotation = atoi(val);
+ /* TODO : handle correctly this case */
+ syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
+ n, h->res_buflen);
}
- else if( strcasecmp(key, "pixelshape") == 0 )
- {
- pixelshape = val;
- } */
- item = strtok_r(NULL, "&,", &saveptr);
- }
- free(path);
-
- if( h->reqflags & FLAG_XFERSTREAMING || h->reqflags & FLAG_RANGE )
- {
- DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with a resized image!\n");
- Send406(h);
- goto resized_error;
- }
-
- DPRINTF(E_INFO, L_HTTP, "Serving resized image for ObjectId: %lld [%s]\n", id, file_path);
-
- /* Figure out the best destination resolution we can use */
- dstw = width;
- dsth = ((((width<<10)/srcw)*srch)>>10);
- if( dsth > height )
- {
- dsth = height;
- dstw = (((height<<10)/srch) * srcw>>10);
- }
- if( dstw <= 640 && dsth <= 480 )
- strcpy(dlna_pn, "SM");
- else if( dstw <= 1024 && dsth <= 768 )
- strcpy(dlna_pn, "MED");
- else
- strcpy(dlna_pn, "LRG");
-
- if( srcw>>3 >= dstw && srch>>3 >= dsth)
- scale = 8;
- else if( srcw>>2 >= dstw && srch>>2 >= dsth )
- scale = 4;
- else if( srcw>>1 >= dstw && srch>>1 >= dsth )
- scale = 2;
-
- strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
- snprintf(header, sizeof(header)-100, "HTTP/1.1 200 OK\r\n"
- "Content-Type: image/jpeg\r\n"
- "Connection: close\r\n"
- "Date: %s\r\n"
- "EXT:\r\n"
- "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
- "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_%s;DLNA.ORG_CI=1\r\n"
- "Server: " MINIDLNA_SERVER_STRING "\r\n",
- date, dlna_pn);
- if( h->reqflags & FLAG_XFERINTERACTIVE )
- {
- strcat(header, "transferMode.dlna.org: Interactive\r\n");
- }
- else if( h->reqflags & FLAG_XFERBACKGROUND )
- {
- strcat(header, "transferMode.dlna.org: Background\r\n");
- }
-
- /* Resizing from a thumbnail is much faster than from a large image */
-#ifdef __sparc__
- tn = result[5];
- if( dstw <= 160 && dsth <= 120 && atoi(tn) )
- {
- l = exif_loader_new();
- exif_loader_write_file(l, file_path);
- ed = exif_loader_get_data(l);
- exif_loader_unref(l);
-
- if( !ed || !ed->size )
- {
- if( ed )
- exif_data_unref(ed);
- DPRINTF(E_WARN, L_HTTP, "Unable to access image thumbnail!\n");
- Send500(h);
- goto resized_error;
- }
- imsrc = image_new_from_jpeg(NULL, 0, (char *)ed->data, ed->size, 1);
- exif_data_unref(ed);
- }
- else
-#endif
- if( strcmp(h->HttpVer, "HTTP/1.0") == 0 )
- {
- imsrc = image_new_from_jpeg(file_path, 1, NULL, 0, scale);
- }
- else
- {
- chunked = 1;
- strcat(header, "Transfer-Encoding: chunked\r\n\r\n");
- }
-
- if( !chunked )
- {
- if( !imsrc )
+ if( h->req_command == EHead )
{
- DPRINTF(E_WARN, L_HTTP, "Unable to open image %s!\n", file_path);
- Send500(h);
- goto resized_error;
+ goto error;
}
- imdst = image_resize(imsrc, dstw, dsth);
- data = image_save_to_jpeg_buf(imdst, &size);
-
- sprintf(str_buf, "Content-Length: %d\r\n\r\n", size);
- strcat(header, str_buf);
- }
-
- if( (send_data(h, header, strlen(header), 0) == 0) && (h->req_command != EHead) )
- {
- if( chunked )
+ n = send(h->socket, data, size, 0);
+ if(n<0)
{
- imsrc = image_new_from_jpeg(file_path, 1, NULL, 0, scale);
- if( !imsrc )
- {
- DPRINTF(E_WARN, L_HTTP, "Unable to open image %s!\n", file_path);
- Send500(h);
- goto resized_error;
- }
- imdst = image_resize(imsrc, dstw, dsth);
- data = image_save_to_jpeg_buf(imdst, &size);
-
- ret = sprintf(str_buf, "%x\r\n", size);
- send_data(h, str_buf, ret, MSG_MORE);
- send_data(h, (char *)data, size, MSG_MORE);
- send_data(h, "\r\n0\r\n\r\n", 7, 0);
- }
- else
+ syslog(LOG_ERR, "send(res_buf): %m");
+ }
+ else if(n < h->res_buflen)
{
- send_data(h, (char *)data, size, 0);
+ /* TODO : handle correctly this case */
+ syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
+ n, h->res_buflen);
}
+ gdFree(data);
}
- DPRINTF(E_INFO, L_HTTP, "Done serving %s\n", file_path);
- if( imsrc )
- image_free(imsrc);
- if( imdst )
- image_free(imdst);
- resized_error:
+ error:
+ gdImageDestroy(imsrc);
+ gdImageDestroy(imdst);
sqlite3_free_table(result);
-#if USE_FORK
- if( !newpid )
- _exit(0);
-#endif
}
+#endif
void
SendResp_dlnafile(struct upnphttp * h, char * object)
@@ -1694,149 +1061,96 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
char hdr_buf[512];
char sql_buf[256];
char **result;
- int rows, ret;
+ int rows;
char date[30];
time_t curtime = time(NULL);
- off_t total, offset, size;
- sqlite_int64 id;
- int sendfh;
- static struct { sqlite_int64 id; char path[PATH_MAX]; char mime[32]; char dlna[96]; } last_file = { 0 };
-#if USE_FORK
- pid_t newpid = 0;
-#endif
+ off_t total;
+ char *path, *mime, *dlna;
+
+ memset(header, 0, 1500);
- id = strtoll(object, NULL, 10);
- if( id != last_file.id )
+ sprintf(sql_buf, "SELECT o.PATH, d.MIME, d.DLNA_PN from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s'", object);
+ sqlite3_get_table(db, sql_buf, &result, &rows, 0, 0);
+ if( !rows )
{
- sprintf(sql_buf, "SELECT PATH, MIME, DLNA_PN from DETAILS where ID = '%lld'", id);
- ret = sql_get_table(db, sql_buf, &result, &rows, NULL);
- if( (ret != SQLITE_OK) )
- {
- DPRINTF(E_ERROR, L_HTTP, "Didn't find valid file for %lld!\n", id);
- Send500(h);
- return;
- }
- if( !rows )
- {
- DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
- sqlite3_free_table(result);
- Send404(h);
- return;
- }
- /* Cache the result */
- last_file.id = id;
- strncpy(last_file.path, result[3], sizeof(last_file.path)-1);
- if( result[4] )
- {
- strncpy(last_file.mime, result[4], sizeof(last_file.mime)-1);
- /* From what I read, Samsung TV's expect a [wrong] MIME type of x-mkv. */
- if( h->req_client == ESamsungTV )
- {
- if( strcmp(last_file.mime+6, "x-matroska") == 0 )
- strcpy(last_file.mime+8, "mkv");
- }
- /* ... and Sony BDP-S370 won't play MKV unless we pretend it's a DiVX file */
- else if( h->req_client == ESonyBDP )
- {
- if( strcmp(last_file.mime+6, "x-matroska") == 0 ||
- strcmp(last_file.mime+6, "mpeg") == 0 )
- strcpy(last_file.mime+6, "divx");
- }
- }
- else
- {
- last_file.mime[0] = '\0';
- }
- if( result[5] )
- snprintf(last_file.dlna, sizeof(last_file.dlna), "DLNA.ORG_PN=%s", result[5]);
- else if( h->reqflags & FLAG_DLNA )
- strcpy(last_file.dlna, dlna_no_conv);
- else
- last_file.dlna[0] = '\0';
- sqlite3_free_table(result);
+ syslog(LOG_NOTICE, "%s not found, responding ERROR 404", object);
+ Send404(h);
+ goto error;
}
-#if USE_FORK
- newpid = fork();
- if( newpid )
- return;
-#endif
- DPRINTF(E_INFO, L_HTTP, "Serving DetailID: %lld [%s]\n", id, last_file.path);
+ path = result[3];
+ mime = result[4];
+ dlna = result[5];
+ printf("ObjectId: %s [%s]\n", object, path);
if( h->reqflags & FLAG_XFERSTREAMING )
{
- if( strncmp(last_file.mime, "image", 5) == 0 )
+ if( strncmp(mime, "imag", 4) == 0 )
{
- DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
+ syslog(LOG_NOTICE, "Hey, you can't specify transferMode as Streaming with an image!");
Send406(h);
goto error;
}
}
- else if( h->reqflags & FLAG_XFERINTERACTIVE )
+ if( h->reqflags & FLAG_XFERINTERACTIVE )
{
if( h->reqflags & FLAG_REALTIMEINFO )
{
- DPRINTF(E_WARN, L_HTTP, "Bad realTimeInfo flag with Interactive request!\n");
+ syslog(LOG_NOTICE, "Bad realTimeInfo flag with Interactive request!");
Send400(h);
goto error;
}
- if( strncmp(last_file.mime, "image", 5) != 0 )
+ if( strncmp(mime, "imag", 4) != 0 )
{
- DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Interactive without an image!\n");
- /* Samsung TVs (well, at least the A950) do this for some reason,
- * and I don't see them fixing this bug any time soon. */
- if( h->req_client != ESamsungTV || GETFLAG(DLNA_STRICT_MASK) )
- {
- Send406(h);
- goto error;
- }
+ syslog(LOG_NOTICE, "Hey, you can't specify transferMode as Interactive without an image!");
+ Send406(h);
+ goto error;
}
}
strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
- offset = h->req_RangeStart;
- sendfh = open(last_file.path, O_RDONLY);
+ off_t offset = h->req_RangeStart;
+ int sendfh = open(path, O_RDONLY);
if( sendfh < 0 ) {
- DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", last_file.path);
+ printf("Error opening %s\n", result[2]);
goto error;
}
- size = lseek(sendfh, 0, SEEK_END);
+ off_t size = lseek(sendfh, 0, SEEK_END);
lseek(sendfh, 0, SEEK_SET);
- sprintf(header, "HTTP/1.1 20%c OK\r\n"
- "Content-Type: %s\r\n", (h->reqflags & FLAG_RANGE ? '6' : '0'), last_file.mime);
if( h->reqflags & FLAG_RANGE )
{
- if( !h->req_RangeEnd )
- h->req_RangeEnd = size;
if( (h->req_RangeStart > h->req_RangeEnd) || (h->req_RangeStart < 0) )
{
- DPRINTF(E_WARN, L_HTTP, "Specified range was invalid!\n");
+ syslog(LOG_NOTICE, "Specified range was invalid!");
Send400(h);
close(sendfh);
goto error;
}
if( h->req_RangeEnd > size )
{
- DPRINTF(E_WARN, L_HTTP, "Specified range was outside file boundaries!\n");
+ syslog(LOG_NOTICE, "Specified range was outside file boundaries!");
Send416(h);
close(sendfh);
goto error;
}
- if( h->req_RangeEnd < size )
+ sprintf(hdr_buf, "HTTP/1.1 206 OK\r\n"
+ "Content-Type: %s\r\n", mime);
+ strcpy(header, hdr_buf);
+ if( h->req_RangeEnd && (h->req_RangeEnd < size) )
{
total = h->req_RangeEnd - h->req_RangeStart + 1;
- sprintf(hdr_buf, "Content-Length: %jd\r\n"
- "Content-Range: bytes %jd-%jd/%jd\r\n",
+ sprintf(hdr_buf, "Content-Length: %llu\r\n"
+ "Content-Range: bytes %lld-%lld/%llu\r\n",
total, h->req_RangeStart, h->req_RangeEnd, size);
}
else
{
h->req_RangeEnd = size;
total = size - h->req_RangeStart;
- sprintf(hdr_buf, "Content-Length: %jd\r\n"
- "Content-Range: bytes %jd-%jd/%jd\r\n",
+ sprintf(hdr_buf, "Content-Length: %llu\r\n"
+ "Content-Range: bytes %lld-%llu/%llu\r\n",
total, h->req_RangeStart, size-1, size);
}
}
@@ -1844,7 +1158,11 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
{
h->req_RangeEnd = size;
total = size;
- sprintf(hdr_buf, "Content-Length: %jd\r\n", total);
+ sprintf(hdr_buf, "%s 200 OK\r\n"
+ "Content-Type: %s\r\n"
+ "Content-Length: %llu\r\n",
+ "HTTP/1.1", mime, total);
+ //h->HttpVer, mime, total);
}
strcat(header, hdr_buf);
@@ -1854,52 +1172,55 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
}
else if( h->reqflags & FLAG_XFERBACKGROUND )
{
- if( strncmp(last_file.mime, "image", 5) == 0 )
+ if( strncmp(mime, "imag", 4) == 0 )
strcat(header, "transferMode.dlna.org: Background\r\n");
}
else //if( h->reqflags & FLAG_XFERINTERACTIVE )
{
- if( (strncmp(last_file.mime, "video", 5) == 0) ||
- (strncmp(last_file.mime, "audio", 5) == 0) )
- {
- strcat(header, "transferMode.dlna.org: Streaming\r\n");
- }
- else
- {
- strcat(header, "transferMode.dlna.org: Interactive\r\n");
- }
- }
-
- if( h->reqflags & FLAG_CAPTION )
- {
- if( sql_get_int_field(db, "SELECT ID from CAPTIONS where ID = '%lld'", id) > 0 )
- {
- sprintf(hdr_buf, "CaptionInfo.sec: http://%s:%d/Captions/%lld.srt\r\n",
- lan_addr[0].str, runtime_vars.port, id);
- strcat(header, hdr_buf);
- }
+ strcat(header, "transferMode.dlna.org: Interactive\r\n");
}
sprintf(hdr_buf, "Accept-Ranges: bytes\r\n"
"Connection: close\r\n"
"Date: %s\r\n"
"EXT:\r\n"
- "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
- "contentFeatures.dlna.org: %s\r\n"
- "Server: " MINIDLNA_SERVER_STRING "\r\n\r\n",
- date, last_file.dlna);
+ "contentFeatures.dlna.org: DLNA.ORG_PN=%s\r\n"
+ "Server: RAIDiator/4.1, UPnP/1.0, MiniDLNA/1.0\r\n\r\n",
+ date, dlna);
strcat(header, hdr_buf);
- if( (send_data(h, header, strlen(header), MSG_MORE) == 0) && (h->req_command != EHead) && (sendfh > 0) )
+ int n;
+ n = send(h->socket, header, strlen(header), 0);
+ if(n<0)
{
- send_file(h, sendfh, offset, h->req_RangeEnd);
+ syslog(LOG_ERR, "send(res_buf): %m");
+ }
+ else if(n < h->res_buflen)
+ {
+ /* TODO : handle correctly this case */
+ syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
+ n, h->res_buflen);
}
- close(sendfh);
+ if( h->req_command == EHead )
+ {
+ close(sendfh);
+ }
+ else if( sendfh > 0 )
+ {
+ while( offset < h->req_RangeEnd ) {
+ int ret = sendfile(h->socket, sendfh, &offset, (h->req_RangeEnd - offset + 1));
+ if( ret == -1 ) {
+ printf("sendfile error :: error no. %d [%s]\n", errno, strerror(errno));
+ if( errno == 32 || errno == 9 || errno == 54 || errno == 104 )
+ break;
+ }
+ else {
+ printf("sent %d bytes to %d. offset is now %d.\n", ret, h->socket, (int)offset);
+ }
+ }
+ close(sendfh);
+ }
error:
-#if USE_FORK
- if( !newpid )
- _exit(0);
-#endif
- return;
+ sqlite3_free_table(result);
}
diff --git a/upnphttp.h b/upnphttp.h
index 74134f7..ffb5a60 100644
--- a/upnphttp.h
+++ b/upnphttp.h
@@ -1,42 +1,19 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#ifndef __UPNPHTTP_H__
#define __UPNPHTTP_H__
#include <netinet/in.h>
#include <sys/queue.h>
-#include "minidlnatypes.h"
#include "config.h"
/* server: HTTP header returned in all HTTP responses : */
-#define MINIDLNA_SERVER_STRING OS_VERSION " DLNADOC/1.50 UPnP/1.0 MiniDLNA/1.0"
+#define MINIUPNPD_SERVER_STRING OS_VERSION " UPnP/1.0 miniupnpd/1.0"
/*
states :
@@ -65,19 +42,20 @@ struct upnphttp {
int req_contentlen;
int req_contentoff; /* header length */
enum httpCommands req_command;
- enum client_types req_client;
const char * req_soapAction;
int req_soapActionLen;
+#ifdef ENABLE_EVENTS
const char * req_Callback; /* For SUBSCRIBE */
int req_CallbackLen;
int req_Timeout;
const char * req_SID; /* For UNSUBSCRIBE */
int req_SIDLen;
- off_t req_RangeStart;
- off_t req_RangeEnd;
+#endif
+ long long int req_RangeStart;
+ long long int req_RangeEnd;
long int req_chunklen;
- uint32_t reqflags;
- uint32_t respflags;
+ int reqflags;
+ int respflags;
/* response */
char * res_buf;
int res_buflen;
@@ -87,32 +65,20 @@ struct upnphttp {
LIST_ENTRY(upnphttp) entries;
};
-#define FLAG_TIMEOUT 0x00000001
-#define FLAG_SID 0x00000002
-#define FLAG_RANGE 0x00000004
-#define FLAG_HOST 0x00000008
-
-#define FLAG_HTML 0x00000080
-#define FLAG_INVALID_REQ 0x00000010
-
-#define FLAG_CHUNKED 0x00000100
-#define FLAG_TIMESEEK 0x00000200
-#define FLAG_REALTIMEINFO 0x00000400
-#define FLAG_PLAYSPEED 0x00000800
-#define FLAG_XFERSTREAMING 0x00001000
-#define FLAG_XFERINTERACTIVE 0x00002000
-#define FLAG_XFERBACKGROUND 0x00004000
-#define FLAG_CAPTION 0x00008000
-
-#define FLAG_DLNA 0x00100000
-#define FLAG_MIME_AVI_DIVX 0x00200000
-#define FLAG_MIME_AVI_AVI 0x00400000
-#define FLAG_MIME_FLAC_FLAC 0x00800000
-#define FLAG_NO_RESIZE 0x01000000
-#define FLAG_MS_PFS 0x02000000 // Microsoft PlaysForSure client
-#define FLAG_AUDIO_ONLY 0x04000000
-
-#define FLAG_FREE_OBJECT_ID 0x00000001
+#define FLAG_TIMEOUT 0x01
+#define FLAG_SID 0x02
+#define FLAG_RANGE 0x04
+#define FLAG_HOST 0x08
+
+#define FLAG_HTML 0x80
+#define FLAG_INVALID_REQ 0x10
+
+#define FLAG_CHUNKED 0x0100
+#define FLAG_TIMESEEK 0x0200
+#define FLAG_REALTIMEINFO 0x0400
+#define FLAG_XFERSTREAMING 0x1000
+#define FLAG_XFERINTERACTIVE 0x2000
+#define FLAG_XFERBACKGROUND 0x4000
/* New_upnphttp() */
struct upnphttp *
@@ -151,25 +117,10 @@ BuildResp2_upnphttp(struct upnphttp * h, int respcode,
const char * respmsg,
const char * body, int bodylen);
-/* Error messages */
-void
-Send500(struct upnphttp *);
-void
-Send501(struct upnphttp *);
-
/* SendResp_upnphttp() */
void
SendResp_upnphttp(struct upnphttp *);
-int
-SearchClientCache(struct in_addr addr, int quiet);
-
-void
-SendResp_icon(struct upnphttp *, char * url);
-void
-SendResp_albumArt(struct upnphttp *, char * url);
-void
-SendResp_caption(struct upnphttp *, char * url);
void
SendResp_resizedimg(struct upnphttp *, char * url);
void
diff --git a/upnpreplyparse.c b/upnpreplyparse.c
index 5bf5311..b19915a 100644
--- a/upnpreplyparse.c
+++ b/upnpreplyparse.c
@@ -1,32 +1,10 @@
/* $Id$ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@@ -38,21 +16,10 @@ static void
NameValueParserStartElt(void * d, const char * name, int l)
{
struct NameValueParserData * data = (struct NameValueParserData *)d;
- if(l>63)
- l = 63;
+ if(l>511)
+ l = 511;
memcpy(data->curelt, name, l);
data->curelt[l] = '\0';
-
- /* store root element */
- if(!data->head.lh_first)
- {
- struct NameValue * nv;
- nv = malloc(sizeof(struct NameValue));
- strcpy(nv->name, "rootElement");
- memcpy(nv->value, name, l);
- nv->value[l] = '\0';
- LIST_INSERT_HEAD( &(data->head), nv, entries);
- }
}
static void
@@ -63,8 +30,8 @@ NameValueParserGetData(void * d, const char * datas, int l)
nv = malloc(sizeof(struct NameValue));
if(l>511)
l = 511;
- strncpy(nv->name, data->curelt, 64);
- nv->name[63] = '\0';
+ strncpy(nv->name, data->curelt, 512);
+ nv->name[511] = '\0';
memcpy(nv->value, datas, l);
nv->value[l] = '\0';
LIST_INSERT_HEAD( &(data->head), nv, entries);
@@ -83,7 +50,7 @@ ParseNameValue(const char * buffer, int bufsize,
parser.starteltfunc = NameValueParserStartElt;
parser.endeltfunc = 0;
parser.datafunc = NameValueParserGetData;
- parser.attfunc = 0;
+ parser.attfunc = 0;
parsexml(&parser);
}
@@ -114,6 +81,31 @@ GetValueFromNameValueList(struct NameValueParserData * pdata,
return p;
}
+#if 0
+/* useless now that minixml ignores namespaces by itself */
+char *
+GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
+ const char * Name)
+{
+ struct NameValue * nv;
+ char * p = NULL;
+ char * pname;
+ for(nv = pdata->head.lh_first;
+ (nv != NULL) && (p == NULL);
+ nv = nv->entries.le_next)
+ {
+ pname = strrchr(nv->name, ':');
+ if(pname)
+ pname++;
+ else
+ pname = nv->name;
+ if(strcmp(pname, Name)==0)
+ p = nv->value;
+ }
+ return p;
+}
+#endif
+
/* debug all-in-one function
* do parsing then display to stdout */
#ifdef DEBUG
diff --git a/upnpreplyparse.h b/upnpreplyparse.h
index 34f0473..8ddb04a 100644
--- a/upnpreplyparse.h
+++ b/upnpreplyparse.h
@@ -1,31 +1,9 @@
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- *
- * Copyright (c) 2006, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#ifndef __UPNPREPLYPARSE_H__
#define __UPNPREPLYPARSE_H__
diff --git a/upnpsoap.c b/upnpsoap.c
index f08ded9..a037a40 100644
--- a/upnpsoap.c
+++ b/upnpsoap.c
@@ -1,51 +1,10 @@
-/* MiniDLNA project
- *
- * http://sourceforge.net/projects/minidlna/
- *
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- *
- * Portions of the code from the MiniUPnP project:
- *
- * Copyright (c) 2006-2007, Thomas Bernard
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
+/* $Id$ */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006-2008 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -53,29 +12,29 @@
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
+#include <syslog.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
-#include <ctype.h>
#include "config.h"
#include "upnpglobalvars.h"
-#include "utils.h"
#include "upnphttp.h"
#include "upnpsoap.h"
#include "upnpreplyparse.h"
#include "getifaddr.h"
-#include "scanner.h"
-#include "sql.h"
-#include "log.h"
+
+#include <ctype.h>
+#include "metadata.h"
+#include <sqlite3.h>
static void
BuildSendAndCloseSoapResp(struct upnphttp * h,
const char * body, int bodylen)
{
static const char beforebody[] =
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
+ "<?xml version=\"1.0\"?>\r\n"
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
"<s:Body>";
@@ -101,6 +60,28 @@ BuildSendAndCloseSoapResp(struct upnphttp * h,
}
static void
+GetStatusInfo(struct upnphttp * h, const char * action)
+{
+ static const char resp[] =
+ "<u:%sResponse "
+ "xmlns:u=\"%s\">"
+ "<NewConnectionStatus>Connected</NewConnectionStatus>"
+ "<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>"
+ "<NewUptime>%ld</NewUptime>"
+ "</u:%sResponse>";
+
+ char body[512];
+ int bodylen;
+ time_t uptime;
+
+ uptime = (time(NULL) - startup_time);
+ bodylen = snprintf(body, sizeof(body), resp,
+ action, "urn:schemas-upnp-org:service:WANIPConnection:1",
+ (long)uptime, action);
+ BuildSendAndCloseSoapResp(h, body, bodylen);
+}
+
+static void
GetSystemUpdateID(struct upnphttp * h, const char * action)
{
static const char resp[] =
@@ -114,7 +95,7 @@ GetSystemUpdateID(struct upnphttp * h, const char * action)
bodylen = snprintf(body, sizeof(body), resp,
action, "urn:schemas-upnp-org:service:ContentDirectory:1",
- updateID, action);
+ 1, action);
BuildSendAndCloseSoapResp(h, body, bodylen);
}
@@ -143,19 +124,44 @@ GetProtocolInfo(struct upnphttp * h, const char * action)
"<u:%sResponse "
"xmlns:u=\"%s\">"
"<Source>"
- RESOURCE_PROTOCOL_INFO_VALUES
+ /*"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=01;DLNA.ORG_CI=1,"
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01,"
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01,"
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01,"
+ "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01,"*/
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,"
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01,"
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED,"
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01,"
+ "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC,"
+ "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01,"
+ "http-get:*:audio/x-ms-wma:*,"
+ "http-get:*:audio/wav:*,"
+ "http-get:*:audio/mp4:*,"
+ "http-get:*:audio/x-aiff:*,"
+ "http-get:*:audio/x-flac:*,"
+ "http-get:*:application/ogg:*,"
+ "http-get:*:image/jpeg:*,"
+ "http-get:*:image/gif:*,"
+ "http-get:*:audio/x-mpegurl:*,"
+ "http-get:*:video/mpeg:*,"
+ "http-get:*:video/x-msvideo:*,"
+ "http-get:*:video/avi:*,"
+ "http-get:*:video/mpeg2:*,"
+ "http-get:*:video/dvd:*,"
+ "http-get:*:video/x-ms-wmv:*"
"</Source>"
"<Sink></Sink>"
"</u:%sResponse>";
- char * body;
+
+ char body[1536];
int bodylen;
- bodylen = asprintf(&body, resp,
+ bodylen = snprintf(body, sizeof(body), resp,
action, "urn:schemas-upnp-org:service:ConnectionManager:1",
action);
BuildSendAndCloseSoapResp(h, body, bodylen);
- free(body);
}
static void
@@ -164,12 +170,7 @@ GetSortCapabilities(struct upnphttp * h, const char * action)
static const char resp[] =
"<u:%sResponse "
"xmlns:u=\"%s\">"
- "<SortCaps>"
- "dc:title,"
- "dc:date,"
- "upnp:class,"
- "upnp:originalTrackNumber"
- "</SortCaps>"
+ "<SortCaps></SortCaps>"
"</u:%sResponse>";
char body[512];
@@ -185,17 +186,9 @@ static void
GetSearchCapabilities(struct upnphttp * h, const char * action)
{
static const char resp[] =
- "<u:%sResponse xmlns:u=\"%s\">"
- "<SearchCaps>"
- "dc:creator,"
- "dc:title,"
- "upnp:album,"
- "upnp:actor,"
- "upnp:artist,"
- "upnp:class,"
- "upnp:genre,"
- "@refID"
- "</SearchCaps>"
+ "<u:%sResponse "
+ "xmlns:u=\"%s\">"
+ "<SearchCaps>dc:title,dc:creator,upnp:class,upnp:artist,upnp:album,@refID</SearchCaps>"
"</u:%sResponse>";
char body[512];
@@ -214,7 +207,7 @@ GetCurrentConnectionIDs(struct upnphttp * h, const char * action)
static const char resp[] =
"<u:%sResponse "
"xmlns:u=\"%s\">"
- "<ConnectionIDs>0</ConnectionIDs>"
+ "<ConnectionIDs>-1</ConnectionIDs>"
"</u:%sResponse>";
char body[512];
@@ -235,11 +228,13 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
"xmlns:u=\"%s\">"
"<RcsID>-1</RcsID>"
"<AVTransportID>-1</AVTransportID>"
- "<ProtocolInfo></ProtocolInfo>"
- "<PeerConnectionManager></PeerConnectionManager>"
+ "<ProtocolInfo>"
+ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,"
+ "</ProtocolInfo>"
+ "<PeerConnectionManager>0</PeerConnectionManager>"
"<PeerConnectionID>-1</PeerConnectionID>"
- "<Direction>Output</Direction>"
- "<Status>Unknown</Status>"
+ "<Direction>0</Direction>"
+ "<Status>0</Status>"
"</u:%sResponse>";
char body[sizeof(resp)+128];
@@ -251,686 +246,149 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
BuildSendAndCloseSoapResp(h, body, bodylen);
}
-static void
-mime_to_ext(const char * mime, char * buf)
-{
- switch( *mime )
- {
- /* Audio extensions */
- case 'a':
- if( strcmp(mime+6, "mpeg") == 0 )
- strcpy(buf, "mp3");
- else if( strcmp(mime+6, "mp4") == 0 )
- strcpy(buf, "m4a");
- else if( strcmp(mime+6, "x-ms-wma") == 0 )
- strcpy(buf, "wma");
- else if( strcmp(mime+6, "x-flac") == 0 )
- strcpy(buf, "flac");
- else if( strcmp(mime+6, "flac") == 0 )
- strcpy(buf, "flac");
- else if( strcmp(mime+6, "x-wav") == 0 )
- strcpy(buf, "wav");
- else if( strncmp(mime+6, "L16", 3) == 0 )
- strcpy(buf, "pcm");
- else if( strcmp(mime+6, "3gpp") == 0 )
- strcpy(buf, "3gp");
- else if( strcmp(mime, "application/ogg") == 0 )
- strcpy(buf, "ogg");
- else
- strcpy(buf, "dat");
- break;
- case 'v':
- if( strcmp(mime+6, "avi") == 0 )
- strcpy(buf, "avi");
- else if( strcmp(mime+6, "divx") == 0 )
- strcpy(buf, "avi");
- else if( strcmp(mime+6, "x-msvideo") == 0 )
- strcpy(buf, "avi");
- else if( strcmp(mime+6, "mpeg") == 0 )
- strcpy(buf, "mpg");
- else if( strcmp(mime+6, "mp4") == 0 )
- strcpy(buf, "mp4");
- else if( strcmp(mime+6, "x-ms-wmv") == 0 )
- strcpy(buf, "wmv");
- else if( strcmp(mime+6, "x-matroska") == 0 )
- strcpy(buf, "mkv");
- else if( strcmp(mime+6, "x-mkv") == 0 )
- strcpy(buf, "mkv");
- else if( strcmp(mime+6, "x-flv") == 0 )
- strcpy(buf, "flv");
- else if( strcmp(mime+6, "vnd.dlna.mpeg-tts") == 0 )
- strcpy(buf, "mpg");
- else if( strcmp(mime+6, "quicktime") == 0 )
- strcpy(buf, "mov");
- else if( strcmp(mime+6, "3gpp") == 0 )
- strcpy(buf, "3gp");
- else if( strcmp(mime+6, "x-tivo-mpeg") == 0 )
- strcpy(buf, "TiVo");
- else
- strcpy(buf, "dat");
- break;
- case 'i':
- if( strcmp(mime+6, "jpeg") == 0 )
- strcpy(buf, "jpg");
- else if( strcmp(mime+6, "png") == 0 )
- strcpy(buf, "png");
- else
- strcpy(buf, "dat");
- break;
- default:
- strcpy(buf, "dat");
- break;
- }
-}
-
-#define FILTER_CHILDCOUNT 0x00000001
-#define FILTER_DC_CREATOR 0x00000002
-#define FILTER_DC_DATE 0x00000004
-#define FILTER_DC_DESCRIPTION 0x00000008
-#define FILTER_DLNA_NAMESPACE 0x00000010
-#define FILTER_REFID 0x00000020
-#define FILTER_RES 0x00000040
-#define FILTER_RES_BITRATE 0x00000080
-#define FILTER_RES_DURATION 0x00000100
-#define FILTER_RES_NRAUDIOCHANNELS 0x00000200
-#define FILTER_RES_RESOLUTION 0x00000400
-#define FILTER_RES_SAMPLEFREQUENCY 0x00000800
-#define FILTER_RES_SIZE 0x00001000
-#define FILTER_UPNP_ACTOR 0x00002000
-#define FILTER_UPNP_ALBUM 0x00004000
-#define FILTER_UPNP_ALBUMARTURI 0x00008000
-#define FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID 0x00010000
-#define FILTER_UPNP_ARTIST 0x00020000
-#define FILTER_UPNP_GENRE 0x00040000
-#define FILTER_UPNP_ORIGINALTRACKNUMBER 0x00080000
-#define FILTER_UPNP_SEARCHCLASS 0x00100000
-
-static u_int32_t
-set_filter_flags(char * filter, enum client_types client)
-{
- char *item, *saveptr = NULL;
- u_int32_t flags = 0;
-
- if( !filter || (strlen(filter) <= 1) )
- return 0xFFFFFFFF;
- if( client == ESamsungTV )
- flags |= FILTER_DLNA_NAMESPACE;
- item = strtok_r(filter, ",", &saveptr);
- while( item != NULL )
- {
- if( saveptr )
- *(item-1) = ',';
- while( isspace(*item) )
- item++;
- if( strcmp(item, "@childCount") == 0 )
- {
- flags |= FILTER_CHILDCOUNT;
- }
- else if( strcmp(item, "dc:creator") == 0 )
- {
- flags |= FILTER_DC_CREATOR;
- }
- else if( strcmp(item, "dc:date") == 0 )
- {
- flags |= FILTER_DC_DATE;
- }
- else if( strcmp(item, "dc:description") == 0 )
- {
- flags |= FILTER_DC_DESCRIPTION;
- }
- else if( strcmp(item, "dlna") == 0 )
- {
- flags |= FILTER_DLNA_NAMESPACE;
- }
- else if( strcmp(item, "@refID") == 0 )
- {
- flags |= FILTER_REFID;
- }
- else if( strcmp(item, "upnp:album") == 0 )
- {
- flags |= FILTER_UPNP_ALBUM;
- }
- else if( strcmp(item, "upnp:albumArtURI") == 0 )
- {
- flags |= FILTER_UPNP_ALBUMARTURI;
- if( client == ESamsungTV )
- flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID;
- }
- else if( strcmp(item, "upnp:albumArtURI@dlna:profileID") == 0 )
- {
- flags |= FILTER_UPNP_ALBUMARTURI;
- flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID;
- }
- else if( strcmp(item, "upnp:artist") == 0 )
- {
- flags |= FILTER_UPNP_ARTIST;
- }
- else if( strcmp(item, "upnp:actor") == 0 )
- {
- flags |= FILTER_UPNP_ACTOR;
- }
- else if( strcmp(item, "upnp:genre") == 0 )
- {
- flags |= FILTER_UPNP_GENRE;
- }
- else if( strcmp(item, "upnp:originalTrackNumber") == 0 )
- {
- flags |= FILTER_UPNP_ORIGINALTRACKNUMBER;
- }
- else if( strcmp(item, "upnp:searchClass") == 0 )
- {
- flags |= FILTER_UPNP_SEARCHCLASS;
- }
- else if( strcmp(item, "res") == 0 )
- {
- flags |= FILTER_RES;
- }
- else if( (strcmp(item, "res@bitrate") == 0) ||
- (strcmp(item, "@bitrate") == 0) ||
- ((strcmp(item, "bitrate") == 0) && (flags & FILTER_RES)) )
- {
- flags |= FILTER_RES;
- flags |= FILTER_RES_BITRATE;
- }
- else if( (strcmp(item, "res@duration") == 0) ||
- (strcmp(item, "@duration") == 0) ||
- ((strcmp(item, "duration") == 0) && (flags & FILTER_RES)) )
- {
- flags |= FILTER_RES;
- flags |= FILTER_RES_DURATION;
- }
- else if( (strcmp(item, "res@nrAudioChannels") == 0) ||
- (strcmp(item, "@nrAudioChannels") == 0) ||
- ((strcmp(item, "nrAudioChannels") == 0) && (flags & FILTER_RES)) )
- {
- flags |= FILTER_RES;
- flags |= FILTER_RES_NRAUDIOCHANNELS;
- }
- else if( (strcmp(item, "res@resolution") == 0) ||
- (strcmp(item, "@resolution") == 0) ||
- ((strcmp(item, "resolution") == 0) && (flags & FILTER_RES)) )
- {
- flags |= FILTER_RES;
- flags |= FILTER_RES_RESOLUTION;
- }
- else if( (strcmp(item, "res@sampleFrequency") == 0) ||
- (strcmp(item, "@sampleFrequency") == 0) ||
- ((strcmp(item, "sampleFrequency") == 0) && (flags & FILTER_RES)) )
- {
- flags |= FILTER_RES;
- flags |= FILTER_RES_SAMPLEFREQUENCY;
- }
- else if( (strcmp(item, "res@size") == 0) ||
- (strcmp(item, "@size") == 0) ||
- (strcmp(item, "size") == 0) )
- {
- flags |= FILTER_RES;
- flags |= FILTER_RES_SIZE;
- }
- item = strtok_r(NULL, ",", &saveptr);
- }
-
- return flags;
-}
-
-char *
-parse_sort_criteria(char * sortCriteria, int * error)
+static int callback(void *args, int argc, char **argv, char **azColName)
{
- char *order = NULL;
- char *item, *saveptr;
- int i, ret, reverse, title_sorted = 0;
- *error = 0;
-
- if( !sortCriteria )
- return NULL;
-
- if( (item = strtok_r(sortCriteria, ",", &saveptr)) )
- {
- order = malloc(4096);
- strcpy(order, "order by ");
- }
- for( i=0; item != NULL; i++ )
- {
- reverse=0;
- if( i )
- strcat(order, ", ");
- if( *item == '+' )
- {
- item++;
- }
- else if( *item == '-' )
- {
- reverse = 1;
- item++;
- }
- if( strcasecmp(item, "upnp:class") == 0 )
- {
- strcat(order, "o.CLASS");
- }
- else if( strcasecmp(item, "dc:title") == 0 )
- {
- strcat(order, "d.TITLE");
- title_sorted = 1;
- }
- else if( strcasecmp(item, "dc:date") == 0 )
- {
- strcat(order, "d.DATE");
- }
- else if( strcasecmp(item, "upnp:originalTrackNumber") == 0 )
- {
- strcat(order, "d.DISC, d.TRACK");
- }
- else
- {
- printf("Unhandled SortCriteria [%s]\n", item);
- *error = 1;
- if( i )
- {
- ret = strlen(order);
- order[ret-2] = '\0';
- }
- i--;
- goto unhandled_order;
- }
-
- if( reverse )
- strcat(order, " DESC");
- unhandled_order:
- item = strtok_r(NULL, ",", &saveptr);
- }
- if( i <= 0 )
- {
- free(order);
- return NULL;
- }
- /* Add a "tiebreaker" sort order */
- if( !title_sorted )
- strcat(order, ", TITLE ASC");
-
- return order;
-}
-
-inline static void
-add_resized_res(int srcw, int srch, int reqw, int reqh, char *dlna_pn,
- char *detailID, struct Response *passed_args)
-{
- int dstw = reqw;
- int dsth = reqh;
-
- if( passed_args->flags & FLAG_NO_RESIZE )
- return;
-
- strcatf(passed_args->str, "&lt;res ");
- if( passed_args->filter & FILTER_RES_RESOLUTION )
- {
- dstw = reqw;
- dsth = ((((reqw<<10)/srcw)*srch)>>10);
- if( dsth > reqh ) {
- dsth = reqh;
- dstw = (((reqh<<10)/srch) * srcw>>10);
- }
- strcatf(passed_args->str, "resolution=\"%dx%d\" ", dstw, dsth);
- }
- strcatf(passed_args->str, "protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=%s;DLNA.ORG_CI=1\"&gt;"
- "http://%s:%d/Resized/%s.jpg?width=%d,height=%d"
- "&lt;/res&gt;",
- dlna_pn, lan_addr[0].str, runtime_vars.port, detailID, dstw, dsth);
-}
-
-inline static void
-add_res(char *size, char *duration, char *bitrate, char *sampleFrequency,
- char *nrAudioChannels, char *resolution, char *dlna_pn, char *mime,
- char *detailID, char *ext, struct Response *passed_args)
-{
- strcatf(passed_args->str, "&lt;res ");
- if( size && (passed_args->filter & FILTER_RES_SIZE) ) {
- strcatf(passed_args->str, "size=\"%s\" ", size);
- }
- if( duration && (passed_args->filter & FILTER_RES_DURATION) ) {
- strcatf(passed_args->str, "duration=\"%s\" ", duration);
- }
- if( bitrate && (passed_args->filter & FILTER_RES_BITRATE) ) {
- strcatf(passed_args->str, "bitrate=\"%s\" ", bitrate);
- }
- if( sampleFrequency && (passed_args->filter & FILTER_RES_SAMPLEFREQUENCY) ) {
- strcatf(passed_args->str, "sampleFrequency=\"%s\" ", sampleFrequency);
- }
- if( nrAudioChannels && (passed_args->filter & FILTER_RES_NRAUDIOCHANNELS) ) {
- strcatf(passed_args->str, "nrAudioChannels=\"%s\" ", nrAudioChannels);
- }
- if( resolution && (passed_args->filter & FILTER_RES_RESOLUTION) ) {
- strcatf(passed_args->str, "resolution=\"%s\" ", resolution);
- }
- strcatf(passed_args->str, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
- "http://%s:%d/MediaItems/%s.%s"
- "&lt;/res&gt;",
- mime, dlna_pn, lan_addr[0].str,
- runtime_vars.port, detailID, ext);
-}
+ struct Response { char *resp; int returned; int requested; int total; char *filter; } *passed_args = (struct Response *)args;
+ char *id = argv[1], *parent = argv[2], *refID = argv[3], *class = argv[4], *name = argv[7], *size = argv[9],
+ *title = argv[10], *duration = argv[11], *bitrate = argv[12], *sampleFrequency = argv[13],
+ *artist = argv[14], *album = argv[15], *genre = argv[16], *comment = argv[17], *nrAudioChannels = argv[18],
+ *track = argv[19], *date = argv[20], *width = argv[21], *height = argv[22], *tn = argv[23],
+ *creator = argv[24], *dlna_pn = argv[25], *mime = argv[26];
+ char dlna_buf[64];
+ char str_buf[4096];
+ //char * str_buf = malloc(4096);
+ char **result;
+ int ret;
-#define SELECT_COLUMNS "SELECT o.OBJECT_ID, o.PARENT_ID, o.REF_ID, o.DETAIL_ID, o.CLASS," \
- " d.SIZE, d.TITLE, d.DURATION, d.BITRATE, d.SAMPLERATE, d.ARTIST," \
- " d.ALBUM, d.GENRE, d.COMMENT, d.CHANNELS, d.TRACK, d.DATE, d.RESOLUTION," \
- " d.THUMBNAIL, d.CREATOR, d.DLNA_PN, d.MIME, d.ALBUM_ART, d.DISC "
+ passed_args->total++;
-static int
-callback(void *args, int argc, char **argv, char **azColName)
-{
- struct Response *passed_args = (struct Response *)args;
- char *id = argv[0], *parent = argv[1], *refID = argv[2], *detailID = argv[3], *class = argv[4], *size = argv[5], *title = argv[6],
- *duration = argv[7], *bitrate = argv[8], *sampleFrequency = argv[9], *artist = argv[10], *album = argv[11],
- *genre = argv[12], *comment = argv[13], *nrAudioChannels = argv[14], *track = argv[15], *date = argv[16], *resolution = argv[17],
- *tn = argv[18], *creator = argv[19], *dlna_pn = argv[20], *mime = argv[21], *album_art = argv[22];
- char dlna_buf[96];
- char ext[5];
- struct string_s *str = passed_args->str;
- int ret = 0;
-
- /* Make sure we have at least 8KB left of allocated memory to finish the response. */
- if( str->off > (str->size - 8192) )
- {
-#if MAX_RESPONSE_SIZE > 0
- if( (str->size+DEFAULT_RESP_SIZE) <= MAX_RESPONSE_SIZE )
- {
-#endif
- str->data = realloc(str->data, (str->off+DEFAULT_RESP_SIZE));
- if( str->data )
- {
- str->size += DEFAULT_RESP_SIZE;
- DPRINTF(E_DEBUG, L_HTTP, "UPnP SOAP response enlarged to %d. [%d results so far]\n",
- str->size, passed_args->returned);
- }
- else
- {
- DPRINTF(E_ERROR, L_HTTP, "UPnP SOAP response was too big, and realloc failed!\n");
- return -1;
- }
-#if MAX_RESPONSE_SIZE > 0
- }
- else
- {
- DPRINTF(E_ERROR, L_HTTP, "UPnP SOAP response cut short, to not exceed the max response size [%lld]!\n", (long long int)MAX_RESPONSE_SIZE);
- return -1;
- }
-#endif
- }
+ if( passed_args->requested && (passed_args->returned >= passed_args->requested) )
+ return 0;
+ //if( (strncmp(class, "item", 4) == 0) && !mime ) // Useless listing if there is no MIME type
+ // return 0;
passed_args->returned++;
if( dlna_pn )
+ //sprintf(dlna_buf, "DLNA.ORG_PN=%s;DLNA.ORG_OP=01", dlna_pn);
sprintf(dlna_buf, "DLNA.ORG_PN=%s", dlna_pn);
- else if( passed_args->flags & FLAG_DLNA )
- strcpy(dlna_buf, dlna_no_conv);
else
strcpy(dlna_buf, "*");
+ /*for(i=0; i<argc; i++){
+ printf("%s = %s\n", azColName[i], argv[i]);
+ }*/
+ //printf("\n");
if( strncmp(class, "item", 4) == 0 )
{
- /* We may need special handling for certain MIME types */
- if( *mime == 'v' )
- {
- if( passed_args->flags & FLAG_MIME_AVI_DIVX )
- {
- if( strcmp(mime, "video/x-msvideo") == 0 )
- {
- if( creator )
- strcpy(mime+6, "divx");
- else
- strcpy(mime+6, "avi");
- }
+ sprintf(str_buf, "&lt;item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent);
+ strcat(passed_args->resp, str_buf);
+ if( refID && (!passed_args->filter || strstr(passed_args->filter, "@refID")) ) {
+ sprintf(str_buf, " refID=\"%s\"", refID);
+ strcat(passed_args->resp, str_buf);
+ }
+ sprintf(str_buf, "&gt;"
+ "&lt;dc:title&gt;%s&lt;/dc:title&gt;"
+ "&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;",
+ title?title:name, class);
+ strcat(passed_args->resp, str_buf);
+ if( comment && (!passed_args->filter || strstr(passed_args->filter, "dc:description")) ) {
+ sprintf(str_buf, "&lt;dc:description&gt;%s&lt;/dc:description&gt;", comment);
+ strcat(passed_args->resp, str_buf);
+ }
+ if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) {
+ sprintf(str_buf, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator);
+ strcat(passed_args->resp, str_buf);
+ }
+ if( date && (!passed_args->filter || strstr(passed_args->filter, "dc:date")) ) {
+ sprintf(str_buf, "&lt;dc:date&gt;%s&lt;/dc:date&gt;", date);
+ strcat(passed_args->resp, str_buf);
+ }
+ if( artist && (!passed_args->filter || strstr(passed_args->filter, "upnp:artist")) ) {
+ sprintf(str_buf, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist);
+ strcat(passed_args->resp, str_buf);
+ }
+ if( album && (!passed_args->filter || strstr(passed_args->filter, "upnp:album")) ) {
+ sprintf(str_buf, "&lt;upnp:album&gt;%s&lt;/upnp:album&gt;", album);
+ strcat(passed_args->resp, str_buf);
+ }
+ if( genre && (!passed_args->filter || strstr(passed_args->filter, "upnp:genre")) ) {
+ sprintf(str_buf, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre);
+ strcat(passed_args->resp, str_buf);
+ }
+ if( track && atoi(track) && (!passed_args->filter || strstr(passed_args->filter, "upnp:originalTrackNumber")) ) {
+ sprintf(str_buf, "&lt;upnp:originalTrackNumber&gt;%s&lt;/upnp:originalTrackNumber&gt;", track);
+ strcat(passed_args->resp, str_buf);
+ }
+ if( !passed_args->filter || strstr(passed_args->filter, "res") ) {
+ strcat(passed_args->resp, "&lt;res ");
+ if( size && (!passed_args->filter || strstr(passed_args->filter, "res@size")) ) {
+ sprintf(str_buf, "size=\"%s\" ", size);
+ strcat(passed_args->resp, str_buf);
}
- else if( passed_args->flags & FLAG_MIME_AVI_AVI )
- {
- if( strcmp(mime, "video/x-msvideo") == 0 )
- {
- strcpy(mime+6, "avi");
- }
+ if( duration && (!passed_args->filter || strstr(passed_args->filter, "res@duration")) ) {
+ sprintf(str_buf, "duration=\"%s\" ", duration);
+ strcat(passed_args->resp, str_buf);
}
- if( !(passed_args->flags & FLAG_DLNA) )
- {
- if( strcmp(mime+6, "vnd.dlna.mpeg-tts") == 0 )
- {
- strcpy(mime+6, "mpeg");
- }
+ if( bitrate && (!passed_args->filter || strstr(passed_args->filter, "res@bitrate")) ) {
+ sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
+ strcat(passed_args->resp, str_buf);
}
- /* From what I read, Samsung TV's expect a [wrong] MIME type of x-mkv. */
- if( passed_args->client == ESamsungTV )
- {
- if( strcmp(mime+6, "x-matroska") == 0 )
- {
- strcpy(mime+8, "mkv");
- }
+ if( sampleFrequency && (!passed_args->filter || strstr(passed_args->filter, "res@sampleFrequency")) ) {
+ sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency);
+ strcat(passed_args->resp, str_buf);
}
- }
- else if( *mime == 'a' )
- {
- if( strcmp(mime+6, "x-flac") == 0 )
- {
- if( passed_args->flags & FLAG_MIME_FLAC_FLAC )
- {
- strcpy(mime+6, "flac");
- }
+ if( nrAudioChannels && (!passed_args->filter || strstr(passed_args->filter, "res@nrAudioChannels")) ) {
+ sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
+ strcat(passed_args->resp, str_buf);
}
- }
-
- ret = strcatf(str, "&lt;item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent);
- if( refID && (passed_args->filter & FILTER_REFID) ) {
- ret = strcatf(str, " refID=\"%s\"", refID);
- }
- ret = strcatf(str, "&gt;"
- "&lt;dc:title&gt;%s&lt;/dc:title&gt;"
- "&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;",
- title, class);
- if( comment && (passed_args->filter & FILTER_DC_DESCRIPTION) ) {
- ret = strcatf(str, "&lt;dc:description&gt;%.384s&lt;/dc:description&gt;", comment);
- }
- if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
- ret = strcatf(str, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator);
- }
- if( date && (passed_args->filter & FILTER_DC_DATE) ) {
- ret = strcatf(str, "&lt;dc:date&gt;%s&lt;/dc:date&gt;", date);
- }
- if( artist ) {
- if( (*mime == 'v') && (passed_args->filter & FILTER_UPNP_ACTOR) ) {
- ret = strcatf(str, "&lt;upnp:actor&gt;%s&lt;/upnp:actor&gt;", artist);
+ if( width && height && (!passed_args->filter || strstr(passed_args->filter, "res@resolution")) ) {
+ sprintf(str_buf, "resolution=\"%sx%s\" ", width, height);
+ strcat(passed_args->resp, str_buf);
}
- if( passed_args->filter & FILTER_UPNP_ARTIST ) {
- ret = strcatf(str, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist);
+ sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
+ "http://%s:5555/MediaItems/%s"
+ "&lt;/res&gt;",
+ mime, dlna_buf, lan_addr[0].str, id);
+ #if 0 //JPEG_RESIZE
+ if( dlna_pn && (strncmp(dlna_pn, "JPEG_LRG", 8) == 0) ) {
+ strcat(passed_args->resp, str_buf);
+ sprintf(str_buf, "&lt;res "
+ "protocolInfo=\"http-get:*:%s:%s\"&gt;"
+ "http://%s:5555/Resized/%s"
+ "&lt;/res&gt;",
+ mime, "DLNA.ORG_PN=JPEG_SM", lan_addr[0].str, id);
}
- }
- if( album && (passed_args->filter & FILTER_UPNP_ALBUM) ) {
- ret = strcatf(str, "&lt;upnp:album&gt;%s&lt;/upnp:album&gt;", album);
- }
- if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
- ret = strcatf(str, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre);
- }
- if( strncmp(id, MUSIC_PLIST_ID, strlen(MUSIC_PLIST_ID)) == 0 ) {
- track = strrchr(id, '$')+1;
- }
- if( track && atoi(track) && (passed_args->filter & FILTER_UPNP_ORIGINALTRACKNUMBER) ) {
- ret = strcatf(str, "&lt;upnp:originalTrackNumber&gt;%s&lt;/upnp:originalTrackNumber&gt;", track);
- }
- if( album_art && atoi(album_art) )
- {
- /* Video and audio album art is handled differently */
- if( *mime == 'v' && (passed_args->filter & FILTER_RES) && !(passed_args->flags & FLAG_MS_PFS) ) {
- ret = strcatf(str, "&lt;res protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN\"&gt;"
- "http://%s:%d/AlbumArt/%s-%s.jpg"
- "&lt;/res&gt;",
- lan_addr[0].str, runtime_vars.port, album_art, detailID);
- } else if( passed_args->filter & FILTER_UPNP_ALBUMARTURI ) {
- ret = strcatf(str, "&lt;upnp:albumArtURI");
- if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) {
- ret = strcatf(str, " dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"");
- }
- ret = strcatf(str, "&gt;http://%s:%d/AlbumArt/%s-%s.jpg&lt;/upnp:albumArtURI&gt;",
- lan_addr[0].str, runtime_vars.port, album_art, detailID);
+ #endif
+ if( tn && atoi(tn) && dlna_pn ) {
+ strcat(passed_args->resp, str_buf);
+ strcat(passed_args->resp, "&lt;res ");
+ sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
+ "http://%s:5555/Thumbnails/%s"
+ "&lt;/res&gt;",
+ mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, id);
}
+ strcat(passed_args->resp, str_buf);
}
-#ifdef PFS_HACK
- if( (passed_args->flags & FLAG_MS_PFS) && *mime == 'i' ) {
- ret = strcatf(str, "&lt;upnp:album&gt;%s&lt;/upnp:album&gt;", "[No Keywords]");
-
- if( tn && atoi(tn) ) {
- ret = strcatf(str, "&lt;upnp:albumArtURI&gt;"
- "http://%s:%d/Thumbnails/%s.jpg"
- "&lt;/upnp:albumArtURI&gt;",
- lan_addr[0].str, runtime_vars.port, detailID);
- } else {
- ret = strcatf(str, "&lt;upnp:albumArtURI&gt;"
- "http://%s:%d/Resized/%s.jpg?width=160,height=160"
- "&lt;/upnp:albumArtURI&gt;",
- lan_addr[0].str, runtime_vars.port, detailID);
- }
- }
-#endif
- if( passed_args->filter & FILTER_RES ) {
- mime_to_ext(mime, ext);
- if( (passed_args->client == EFreeBox) && tn && atoi(tn) ) {
- ret = strcatf(str, "&lt;res protocolInfo=\"http-get:*:%s:%s\"&gt;"
- "http://%s:%d/Thumbnails/%s.jpg"
- "&lt;/res&gt;",
- mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str,
- runtime_vars.port, detailID);
- }
- add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
- resolution, dlna_buf, mime, detailID, ext, passed_args);
- if( (*mime == 'i') && (passed_args->client != EFreeBox) ) {
-#if 1 //JPEG_RESIZE
- int srcw = atoi(strsep(&resolution, "x"));
- int srch = atoi(resolution);
- if( !dlna_pn ) {
- add_resized_res(srcw, srch, 4096, 4096, "JPEG_LRG", detailID, passed_args);
- }
- if( !dlna_pn || !strncmp(dlna_pn, "JPEG_L", 6) || !strncmp(dlna_pn, "JPEG_M", 6) ) {
- add_resized_res(srcw, srch, 640, 480, "JPEG_SM", detailID, passed_args);
- }
-#endif
- if( tn && atoi(tn) ) {
- ret = strcatf(str, "&lt;res protocolInfo=\"http-get:*:%s:%s\"&gt;"
- "http://%s:%d/Thumbnails/%s.jpg"
- "&lt;/res&gt;",
- mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str,
- runtime_vars.port, detailID);
- }
- }
- else if( *mime == 'v' ) {
- switch( passed_args->client ) {
- case EToshibaTV:
- if( dlna_pn &&
- (strncmp(dlna_pn, "MPEG_TS_HD_NA", 13) == 0 ||
- strncmp(dlna_pn, "MPEG_TS_SD_NA", 13) == 0 ||
- strncmp(dlna_pn, "AVC_TS_MP_HD_AC3", 16) == 0 ||
- strncmp(dlna_pn, "AVC_TS_HP_HD_AC3", 16) == 0))
- {
- sprintf(dlna_buf, "DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
- add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
- resolution, dlna_buf, mime, detailID, ext, passed_args);
- }
- break;
- case ESonyBDP:
- if( dlna_pn &&
- (strncmp(dlna_pn, "AVC_TS", 6) == 0 ||
- strncmp(dlna_pn, "MPEG_TS", 7) == 0) )
- {
- if( strncmp(dlna_pn, "MPEG_TS_SD_NA", 13) != 0 )
- {
- sprintf(dlna_buf, "DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
- add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
- resolution, dlna_buf, mime, detailID, ext, passed_args);
- }
- if( strncmp(dlna_pn, "MPEG_TS_SD_EU", 13) != 0 )
- {
- sprintf(dlna_buf, "DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
- add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
- resolution, dlna_buf, mime, detailID, ext, passed_args);
- }
- }
- else if( (dlna_pn &&
- (strncmp(dlna_pn, "AVC_MP4", 7) == 0 ||
- strncmp(dlna_pn, "MPEG4_P2_MP4", 12) == 0)) ||
- strcmp(mime+6, "x-matroska") == 0 ||
- strcmp(mime+6, "x-msvideo") == 0 ||
- strcmp(mime+6, "mpeg") == 0 )
- {
- strcpy(mime+6, "avi");
- if( !dlna_pn || strncmp(dlna_pn, "MPEG_PS_NTSC", 12) != 0 )
- {
- sprintf(dlna_buf, "DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
- add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
- resolution, dlna_buf, mime, detailID, ext, passed_args);
- }
- if( !dlna_pn || strncmp(dlna_pn, "MPEG_PS_PAL", 11) != 0 )
- {
- sprintf(dlna_buf, "DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
- add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
- resolution, dlna_buf, mime, detailID, ext, passed_args);
- }
- }
- break;
- case ESonyBravia:
- /* BRAVIA KDL-##*X### series TVs do natively support AVC/AC3 in TS, but
- require profile to be renamed (applies to _T and _ISO variants also) */
- if( dlna_pn &&
- (strncmp(dlna_pn, "AVC_TS_MP_SD_AC3", 16) == 0 ||
- strncmp(dlna_pn, "AVC_TS_MP_HD_AC3", 16) == 0 ||
- strncmp(dlna_pn, "AVC_TS_HP_HD_AC3", 16) == 0))
- {
- sprintf(dlna_buf, "DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
- add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
- resolution, dlna_buf, mime, detailID, ext, passed_args);
- }
- break;
- default:
- break;
- }
- }
- }
- ret = strcatf(str, "&lt;/item&gt;");
+ strcpy(str_buf, "&lt;/item&gt;");
}
else if( strncmp(class, "container", 9) == 0 )
{
- ret = strcatf(str, "&lt;container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
- if( passed_args->filter & FILTER_CHILDCOUNT )
- {
- int children;
- ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s';", id);
- children = (ret > 0) ? ret : 0;
- ret = strcatf(str, "childCount=\"%d\"", children);
- }
- /* If the client calls for BrowseMetadata on root, we have to include our "upnp:searchClass"'s, unless they're filtered out */
- if( (passed_args->requested == 1) && (strcmp(id, "0") == 0) )
- {
- if( passed_args->filter & FILTER_UPNP_SEARCHCLASS )
- {
- ret = strcatf(str, "&gt;"
- "&lt;upnp:searchClass includeDerived=\"1\"&gt;object.item.audioItem&lt;/upnp:searchClass&gt;"
- "&lt;upnp:searchClass includeDerived=\"1\"&gt;object.item.imageItem&lt;/upnp:searchClass&gt;"
- "&lt;upnp:searchClass includeDerived=\"1\"&gt;object.item.videoItem&lt;/upnp:searchClass");
- }
- }
- ret = strcatf(str, "&gt;"
- "&lt;dc:title&gt;%s&lt;/dc:title&gt;"
- "&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;",
- title, class);
- if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
- ret = strcatf(str, "&lt;dc:creator&gt;%s&lt;/dc:creator&gt;", creator);
- }
- if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
- ret = strcatf(str, "&lt;upnp:genre&gt;%s&lt;/upnp:genre&gt;", genre);
- }
- if( artist && (passed_args->filter & FILTER_UPNP_ARTIST) ) {
- ret = strcatf(str, "&lt;upnp:artist&gt;%s&lt;/upnp:artist&gt;", artist);
- }
- if( album_art && atoi(album_art) && (passed_args->filter & FILTER_UPNP_ALBUMARTURI) ) {
- ret = strcatf(str, "&lt;upnp:albumArtURI ");
- if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) {
- ret = strcatf(str, "dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"");
- }
- ret = strcatf(str, "&gt;http://%s:%d/AlbumArt/%s-%s.jpg&lt;/upnp:albumArtURI&gt;",
- lan_addr[0].str, runtime_vars.port, album_art, detailID);
- }
- ret = strcatf(str, "&lt;/container&gt;");
+ sprintf(str_buf, "SELECT count(*) from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where PARENT_ID = '%s' order by d.TRACK, d.TITLE, o.NAME;", id);
+ ret = sqlite3_get_table(db, str_buf, &result, 0, 0, 0);
+ sprintf(str_buf, "&lt;container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
+ strcat(passed_args->resp, str_buf);
+ if( !passed_args->filter || strstr(passed_args->filter, "@childCount")) {
+ sprintf(str_buf, "childCount=\"%s\"", result[1]);
+ strcat(passed_args->resp, str_buf);
+ }
+ sprintf(str_buf, "&gt;"
+ "&lt;dc:title&gt;%s&lt;/dc:title&gt;"
+ "&lt;upnp:class&gt;object.%s&lt;/upnp:class&gt;"
+ "&lt;/container&gt;",
+ name, class);
+ sqlite3_free_table(result);
}
+ strcat(passed_args->resp, str_buf);
return 0;
}
@@ -942,176 +400,80 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
"<u:BrowseResponse "
"xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">"
"<Result>"
- "&lt;DIDL-Lite"
- CONTENT_DIRECTORY_SCHEMAS;
+ "&lt;DIDL-Lite xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\"&gt;\n";
+ static const char resp1[] = "&lt;/DIDL-Lite&gt;</Result>";
+ static const char resp2[] = "<UpdateID>0</UpdateID></u:BrowseResponse>";
+
+ char *resp = calloc(1, 1048576);
+ strcpy(resp, resp0);
+
+ char str_buf[4096];
+ char str_buf2[4096];
+ memset(str_buf, '\0', sizeof(str_buf));
+ memset(str_buf2, '\0', sizeof(str_buf2));
char *zErrMsg = 0;
- char *sql, *ptr;
int ret;
- struct Response args;
- struct string_s str;
- int totalMatches;
- struct NameValueParserData data;
+ char sql_buf[4096];
+ struct Response { char *resp; int returned; int requested; int total; char *filter; } args;
+ struct NameValueParserData data;
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
+ int RequestedCount = atoi( GetValueFromNameValueList(&data, "RequestedCount") );
+ int StartingIndex = atoi( GetValueFromNameValueList(&data, "StartingIndex") );
char * ObjectId = GetValueFromNameValueList(&data, "ObjectID");
char * Filter = GetValueFromNameValueList(&data, "Filter");
char * BrowseFlag = GetValueFromNameValueList(&data, "BrowseFlag");
char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria");
- char * orderBy = NULL;
- int RequestedCount = 0;
- int StartingIndex = 0;
- if( (ptr = GetValueFromNameValueList(&data, "RequestedCount")) )
- RequestedCount = atoi(ptr);
- if( !RequestedCount )
- RequestedCount = -1;
- if( (ptr = GetValueFromNameValueList(&data, "StartingIndex")) )
- StartingIndex = atoi(ptr);
- if( !BrowseFlag || (strcmp(BrowseFlag, "BrowseDirectChildren") && strcmp(BrowseFlag, "BrowseMetadata")) )
- {
- SoapError(h, 402, "Invalid Args");
- if( h->reqflags & FLAG_MS_PFS )
- ObjectId = sqlite3_malloc(1);
- goto browse_error;
- }
- if( !ObjectId && !(ObjectId = GetValueFromNameValueList(&data, "ContainerID")) )
- {
- SoapError(h, 701, "No such object error");
- if( h->reqflags & FLAG_MS_PFS )
- ObjectId = sqlite3_malloc(1);
- goto browse_error;
- }
- memset(&args, 0, sizeof(args));
- memset(&str, 0, sizeof(str));
-
- str.data = malloc(DEFAULT_RESP_SIZE);
- str.size = DEFAULT_RESP_SIZE;
- str.off = sprintf(str.data, "%s", resp0);
- args.str = &str;
- /* See if we need to include DLNA namespace reference */
- args.filter = set_filter_flags(Filter, h->req_client);
- if( args.filter & FILTER_DLNA_NAMESPACE )
- {
- ret = strcatf(&str, DLNA_NAMESPACE);
- }
- strcatf(&str, "&gt;\n");
+ if( !ObjectId )
+ ObjectId = GetValueFromNameValueList(&data, "ContainerID");
+ memset(&args, 0, sizeof(args));
+ args.total = 0;
args.returned = 0;
args.requested = RequestedCount;
- args.client = h->req_client;
- args.flags = h->reqflags;
- if( args.flags & FLAG_MS_PFS )
- {
- if( !strchr(ObjectId, '$') && (strcmp(ObjectId, "0") != 0) )
- {
- ptr = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS"
- " where OBJECT_ID in "
- "('"MUSIC_ID"$%s', '"VIDEO_ID"$%s', '"IMAGE_ID"$%s')",
- ObjectId, ObjectId, ObjectId);
- if( ptr )
- {
- ObjectId = ptr;
- args.flags |= FLAG_FREE_OBJECT_ID;
- }
- }
- }
- DPRINTF(E_DEBUG, L_HTTP, "Browsing ContentDirectory:\n"
- " * ObjectID: %s\n"
- " * Count: %d\n"
- " * StartingIndex: %d\n"
- " * BrowseFlag: %s\n"
- " * Filter: %s\n"
- " * SortCriteria: %s\n",
- ObjectId, RequestedCount, StartingIndex,
- BrowseFlag, Filter, SortCriteria);
-
- if( (args.flags & FLAG_AUDIO_ONLY) && (strcmp(ObjectId, "0") == 0) )
+ args.resp = NULL;
+ args.filter = NULL;
+ printf("Asked for ObjectID: %s\n", ObjectId);
+ printf("Asked for Count: %d\n", RequestedCount);
+ printf("Asked for StartingIndex: %d\n", StartingIndex);
+ printf("Asked for BrowseFlag: %s\n", BrowseFlag);
+ printf("Asked for Filter: %s\n", Filter);
+ if( SortCriteria ) printf("Asked for SortCriteria: %s\n", SortCriteria);
+
+ if( !Filter )
{
- ObjectId = sqlite3_mprintf("%s", MUSIC_ID);
- args.flags |= FLAG_FREE_OBJECT_ID;
+ ClearNameValueList(&data);
+ SoapError(h, 402, "Invalid Args");
+ return;
}
+ if( strlen(Filter) > 1 )
+ args.filter = Filter;
- if( strcmp(BrowseFlag+6, "Metadata") == 0 )
+ args.resp = resp;
+ if( strcmp(BrowseFlag, "BrowseMetadata") == 0 )
{
args.requested = 1;
- sql = sqlite3_mprintf( SELECT_COLUMNS
- "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
- " where OBJECT_ID = '%s';"
- , ObjectId);
- ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);
- totalMatches = args.returned;
+ sprintf(sql_buf, "SELECT * from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where OBJECT_ID = '%s';", ObjectId);
+ ret = sqlite3_exec(db, sql_buf, callback, (void *) &args, &zErrMsg);
}
else
{
- ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", ObjectId);
- totalMatches = (ret > 0) ? ret : 0;
- ret = 0;
- if( SortCriteria )
- {
-#ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */
- if( totalMatches < 10000 )
-#endif
- orderBy = parse_sort_criteria(SortCriteria, &ret);
- }
- else
- {
- if( strncmp(ObjectId, MUSIC_PLIST_ID, strlen(MUSIC_PLIST_ID)) == 0 )
- {
- if( strcmp(ObjectId, MUSIC_PLIST_ID) == 0 )
- asprintf(&orderBy, "order by d.TITLE");
- else
- asprintf(&orderBy, "order by length(OBJECT_ID), OBJECT_ID");
- }
- else if( args.client == ERokuSoundBridge )
- {
-#ifdef __sparc__
- if( totalMatches < 10000 )
-#endif
- asprintf(&orderBy, "order by o.CLASS, d.DISC, d.TRACK, d.TITLE");
- }
- }
- /* If it's a DLNA client, return an error for bad sort criteria */
- if( (args.flags & FLAG_DLNA) && ret )
- {
- SoapError(h, 709, "Unsupported or invalid sort criteria");
- goto browse_error;
- }
-
- sql = sqlite3_mprintf( SELECT_COLUMNS
- "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
- " where PARENT_ID = '%s' %s limit %d, %d;",
- ObjectId, orderBy, StartingIndex, RequestedCount);
- DPRINTF(E_DEBUG, L_HTTP, "Browse SQL: %s\n", sql);
- ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);
+ sprintf(sql_buf, "SELECT * from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
+ " where PARENT_ID = '%s' order by d.TRACK, d.TITLE, o.NAME limit %d, -1;",
+ ObjectId, StartingIndex);
+ ret = sqlite3_exec(db, sql_buf, callback, (void *) &args, &zErrMsg);
}
- sqlite3_free(sql);
- if( (ret != SQLITE_OK) && (zErrMsg != NULL) )
- {
- DPRINTF(E_WARN, L_HTTP, "SQL error: %s\nBAD SQL: %s\n", zErrMsg, sql);
+ if( ret != SQLITE_OK ){
+ printf("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
- /* Does the object even exist? */
- if( !totalMatches )
- {
- ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where OBJECT_ID = '%s'", ObjectId);
- if( ret <= 0 )
- {
- SoapError(h, 701, "No such object error");
- goto browse_error;
- }
- }
- ret = strcatf(&str, "&lt;/DIDL-Lite&gt;</Result>\n"
- "<NumberReturned>%u</NumberReturned>\n"
- "<TotalMatches>%u</TotalMatches>\n"
- "<UpdateID>%u</UpdateID>"
- "</u:BrowseResponse>",
- args.returned, totalMatches, updateID);
- BuildSendAndCloseSoapResp(h, str.data, str.off);
-browse_error:
+ strcat(resp, resp1);
+ sprintf(str_buf, "\n<NumberReturned>%u</NumberReturned>\n<TotalMatches>%u</TotalMatches>\n", args.returned, args.total);
+ strcat(resp, str_buf);
+ strcat(resp, resp2);
+ BuildSendAndCloseSoapResp(h, resp, strlen(resp));
ClearNameValueList(&data);
- if( args.flags & FLAG_FREE_OBJECT_ID )
- sqlite3_free(ObjectId);
- free(orderBy);
- free(str.data);
+ free(resp);
}
static void
@@ -1121,232 +483,136 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
"<u:SearchResponse "
"xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">"
"<Result>"
- "&lt;DIDL-Lite"
- CONTENT_DIRECTORY_SCHEMAS;
- char *zErrMsg = 0;
- char *sql, *ptr;
- char **result;
- struct Response args;
- struct string_s str;
- int totalMatches = 0;
- int ret;
+ "&lt;DIDL-Lite xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\"&gt;\n";
+ static const char resp1[] = "&lt;/DIDL-Lite&gt;</Result>";
+ static const char resp2[] = "<UpdateID>0</UpdateID></u:SearchResponse>";
+
+ char *resp = calloc(8, 16384);
+ strcpy(resp, resp0);
+
+ char str_buf[4096];
+ char str_buf2[4096];
+ memset(str_buf, '\0', sizeof(str_buf));
+ memset(str_buf2, '\0', sizeof(str_buf2));
+ struct Response { char *resp; int returned; int requested; int total; char *filter; } args;
struct NameValueParserData data;
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
+ int RequestedCount = atoi( GetValueFromNameValueList(&data, "RequestedCount") );
+ int StartingIndex = atoi( GetValueFromNameValueList(&data, "StartingIndex") );
char * ContainerID = GetValueFromNameValueList(&data, "ContainerID");
char * Filter = GetValueFromNameValueList(&data, "Filter");
char * SearchCriteria = GetValueFromNameValueList(&data, "SearchCriteria");
char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria");
- char * newSearchCriteria = NULL;
- char * orderBy = NULL;
- char groupBy[] = "group by DETAIL_ID";
- int RequestedCount = 0;
- int StartingIndex = 0;
- if( (ptr = GetValueFromNameValueList(&data, "RequestedCount")) )
- RequestedCount = atoi(ptr);
- if( !RequestedCount )
- RequestedCount = -1;
- if( (ptr = GetValueFromNameValueList(&data, "StartingIndex")) )
- StartingIndex = atoi(ptr);
- if( !ContainerID )
- {
- if( !(ContainerID = GetValueFromNameValueList(&data, "ObjectID")) )
- {
- SoapError(h, 701, "No such object error");
- if( h->reqflags & FLAG_MS_PFS )
- ContainerID = sqlite3_malloc(1);
- goto search_error;
- }
- }
- memset(&args, 0, sizeof(args));
- memset(&str, 0, sizeof(str));
-
- str.data = malloc(DEFAULT_RESP_SIZE);
- str.size = DEFAULT_RESP_SIZE;
- str.off = sprintf(str.data, "%s", resp0);
- /* See if we need to include DLNA namespace reference */
- args.filter = set_filter_flags(Filter, h->req_client);
- if( args.filter & FILTER_DLNA_NAMESPACE )
- {
- ret = strcatf(&str, DLNA_NAMESPACE);
- }
- ret = strcatf(&str, "&gt;\n");
+ memset(&args, 0, sizeof(args));
+ args.total = 0;
args.returned = 0;
args.requested = RequestedCount;
- args.client = h->req_client;
- args.flags = h->reqflags;
- args.str = &str;
- if( h->reqflags & FLAG_MS_PFS )
+ args.resp = NULL;
+ args.filter = NULL;
+ printf("Asked for ContainerID: %s\n", ContainerID);
+ printf("Asked for Count: %d\n", RequestedCount);
+ printf("Asked for StartingIndex: %d\n", StartingIndex);
+ printf("Asked for SearchCriteria: %s\n", SearchCriteria);
+ printf("Asked for Filter: %s\n", Filter);
+ if( SortCriteria ) printf("Asked for SortCriteria: %s\n", SortCriteria);
+
+ if( !Filter )
{
- if( strchr(ContainerID, '$') || (strcmp(ContainerID, "0") == 0) )
- {
- ContainerID = sqlite3_mprintf("%s", ContainerID);
- }
- else
- {
- ptr = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS"
- " where OBJECT_ID in "
- "('"MUSIC_ID"$%s', '"VIDEO_ID"$%s', '"IMAGE_ID"$%s')",
- ContainerID, ContainerID, ContainerID);
- if( ptr )
- ContainerID = ptr;
- else
- ContainerID = sqlite3_mprintf("%s", ContainerID);
- }
- #if 0 // Looks like the 360 already does this
- /* Sort by track number for some containers */
- if( orderBy &&
- ((strncmp(ContainerID, MUSIC_GENRE_ID, 3) == 0) ||
- (strncmp(ContainerID, MUSIC_ARTIST_ID, 3) == 0) ||
- (strncmp(ContainerID, MUSIC_ALBUM_ID, 3) == 0)) )
- {
- DPRINTF(E_DEBUG, L_HTTP, "Old sort order: %s\n", orderBy);
- sprintf(str_buf, "d.TRACK, ");
- memmove(orderBy+18, orderBy+9, strlen(orderBy)+1);
- memmove(orderBy+9, &str_buf, 9);
- DPRINTF(E_DEBUG, L_HTTP, "New sort order: %s\n", orderBy);
- }
- #endif
+ ClearNameValueList(&data);
+ SoapError(h, 402, "Invalid Args");
+ return;
}
- DPRINTF(E_DEBUG, L_HTTP, "Searching ContentDirectory:\n"
- " * ObjectID: %s\n"
- " * Count: %d\n"
- " * StartingIndex: %d\n"
- " * SearchCriteria: %s\n"
- " * Filter: %s\n"
- " * SortCriteria: %s\n",
- ContainerID, RequestedCount, StartingIndex,
- SearchCriteria, Filter, SortCriteria);
-
+ if( strlen(Filter) > 1 )
+ args.filter = Filter;
if( strcmp(ContainerID, "0") == 0 )
- *ContainerID = '*';
- else if( strcmp(ContainerID, MUSIC_ALL_ID) == 0 )
- groupBy[0] = '\0';
+ *ContainerID = '%';
if( !SearchCriteria )
{
- newSearchCriteria = strdup("1 = 1");
- SearchCriteria = newSearchCriteria;
+ asprintf(&SearchCriteria, "1 = 1");
}
else
{
SearchCriteria = modifyString(SearchCriteria, "&quot;", "\"", 0);
SearchCriteria = modifyString(SearchCriteria, "&apos;", "'", 0);
- SearchCriteria = modifyString(SearchCriteria, "\\\"", "\"\"", 0);
- SearchCriteria = modifyString(SearchCriteria, "object.", "", 0);
SearchCriteria = modifyString(SearchCriteria, "derivedfrom", "like", 1);
- SearchCriteria = modifyString(SearchCriteria, "contains", "like", 2);
+ SearchCriteria = modifyString(SearchCriteria, "contains", "like", 1);
SearchCriteria = modifyString(SearchCriteria, "dc:title", "d.TITLE", 0);
SearchCriteria = modifyString(SearchCriteria, "dc:creator", "d.CREATOR", 0);
SearchCriteria = modifyString(SearchCriteria, "upnp:class", "o.CLASS", 0);
- SearchCriteria = modifyString(SearchCriteria, "upnp:actor", "d.ARTIST", 0);
SearchCriteria = modifyString(SearchCriteria, "upnp:artist", "d.ARTIST", 0);
SearchCriteria = modifyString(SearchCriteria, "upnp:album", "d.ALBUM", 0);
- SearchCriteria = modifyString(SearchCriteria, "upnp:genre", "d.GENRE", 0);
SearchCriteria = modifyString(SearchCriteria, "exists true", "is not NULL", 0);
SearchCriteria = modifyString(SearchCriteria, "exists false", "is NULL", 0);
SearchCriteria = modifyString(SearchCriteria, "@refID", "REF_ID", 0);
- if( strstr(SearchCriteria, "@id") )
- {
- newSearchCriteria = strdup(SearchCriteria);
- SearchCriteria = newSearchCriteria = modifyString(newSearchCriteria, "@id", "OBJECT_ID", 0);
- }
- if( strstr(SearchCriteria, "res is ") )
- {
- if( !newSearchCriteria )
- newSearchCriteria = strdup(SearchCriteria);
- SearchCriteria = newSearchCriteria = modifyString(newSearchCriteria, "res is ", "MIME is ", 0);
- }
- #if 0 // Does 360 need this?
- if( strstr(SearchCriteria, "&amp;") )
- {
- if( newSearchCriteria )
- newSearchCriteria = modifyString(newSearchCriteria, "&amp;", "&amp;amp;", 0);
- else
- newSearchCriteria = modifyString(strdup(SearchCriteria), "&amp;", "&amp;amp;", 0);
- SearchCriteria = newSearchCriteria;
- }
- #endif
+ SearchCriteria = modifyString(SearchCriteria, "object.", "", 0);
}
- DPRINTF(E_DEBUG, L_HTTP, "Translated SearchCriteria: %s\n", SearchCriteria);
-
- sql = sqlite3_mprintf("SELECT (select count(distinct DETAIL_ID)"
- " from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)"
- " where (OBJECT_ID glob '%s$*') and (%s))"
- " + "
- "(select count(*) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)"
- " where (OBJECT_ID = '%s') and (%s))",
- ContainerID, SearchCriteria, ContainerID, SearchCriteria);
- //DEBUG DPRINTF(E_DEBUG, L_HTTP, "Count SQL: %s\n", sql);
- ret = sql_get_table(db, sql, &result, NULL, NULL);
- sqlite3_free(sql);
- if( ret == SQLITE_OK )
- {
- totalMatches = atoi(result[1]);
- sqlite3_free_table(result);
+ printf("Asked for SearchCriteria: %s\n", SearchCriteria);
+
+ char *zErrMsg = 0;
+ int ret;
+ char sql_buf[4096];
+ args.resp = resp;
+ sprintf(sql_buf, "SELECT * from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
+ " where OBJECT_ID like '%s$%%' and (%s) order by d.TRACK, d.TITLE, o.NAME limit %d, -1;",
+ ContainerID, SearchCriteria, StartingIndex);
+ printf("Search SQL: %s\n", sql_buf);
+ ret = sqlite3_exec(db, sql_buf, callback, (void *) &args, &zErrMsg);
+ if( ret != SQLITE_OK ){
+ printf("SQL error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
}
- else
+ strcat(resp, resp1);
+ sprintf(str_buf, "\n<NumberReturned>%u</NumberReturned>\n<TotalMatches>%u</TotalMatches>\n", args.returned, args.total);
+ strcat(resp, str_buf);
+ strcat(resp, resp2);
+ BuildSendAndCloseSoapResp(h, resp, strlen(resp));
+ ClearNameValueList(&data);
+ free(resp);
+}
+
+static void
+GetExternalIPAddress(struct upnphttp * h, const char * action)
+{
+ static const char resp[] =
+ "<u:%sResponse "
+ "xmlns:u=\"%s\">"
+ "<NewExternalIPAddress>%s</NewExternalIPAddress>"
+ "</u:%sResponse>";
+
+ char body[512];
+ int bodylen;
+ char ext_ip_addr[INET_ADDRSTRLEN];
+
+#ifndef MULTIPLE_EXTERNAL_IP
+ if(use_ext_ip_addr)
{
- /* Must be invalid SQL, so most likely bad or unhandled search criteria. */
- SoapError(h, 708, "Unsupported or invalid search criteria");
- goto search_error;
+ strncpy(ext_ip_addr, use_ext_ip_addr, INET_ADDRSTRLEN);
}
- /* Does the object even exist? */
- if( !totalMatches )
+ else if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0)
+ {
+ syslog(LOG_ERR, "Failed to get ip address for interface %s",
+ ext_if_name);
+ strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
+ }
+#else
+ int i;
+ strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
+ for(i = 0; i<n_lan_addr; i++)
{
- ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where OBJECT_ID = '%q'",
- !strcmp(ContainerID, "*")?"0":ContainerID);
- if( ret <= 0 )
+ if( (h->clientaddr.s_addr & lan_addr[i].mask.s_addr)
+ == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
{
- SoapError(h, 710, "No such container");
- goto search_error;
+ strncpy(ext_ip_addr, lan_addr[i].ext_ip_str, INET_ADDRSTRLEN);
+ break;
}
}
-#ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */
- ret = 0;
- if( totalMatches < 10000 )
#endif
- orderBy = parse_sort_criteria(SortCriteria, &ret);
- /* If it's a DLNA client, return an error for bad sort criteria */
- if( (args.flags & FLAG_DLNA) && ret )
- {
- SoapError(h, 709, "Unsupported or invalid sort criteria");
- goto search_error;
- }
-
- sql = sqlite3_mprintf( SELECT_COLUMNS
- "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
- " where OBJECT_ID glob '%s$*' and (%s) %s "
- "%z %s"
- " limit %d, %d",
- ContainerID, SearchCriteria, groupBy,
- (*ContainerID == '*') ? NULL :
- sqlite3_mprintf("UNION ALL " SELECT_COLUMNS
- "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
- " where OBJECT_ID = '%s' and (%s) ", ContainerID, SearchCriteria),
- orderBy, StartingIndex, RequestedCount);
- DPRINTF(E_DEBUG, L_HTTP, "Search SQL: %s\n", sql);
- ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);
- if( (ret != SQLITE_OK) && (zErrMsg != NULL) )
- {
- DPRINTF(E_WARN, L_HTTP, "SQL error: %s\nBAD SQL: %s\n", zErrMsg, sql);
- sqlite3_free(zErrMsg);
- }
- sqlite3_free(sql);
- ret = strcatf(&str, "&lt;/DIDL-Lite&gt;</Result>\n"
- "<NumberReturned>%u</NumberReturned>\n"
- "<TotalMatches>%u</TotalMatches>\n"
- "<UpdateID>%u</UpdateID>"
- "</u:SearchResponse>",
- args.returned, totalMatches, updateID);
- BuildSendAndCloseSoapResp(h, str.data, str.off);
-search_error:
- ClearNameValueList(&data);
- if( h->reqflags & FLAG_MS_PFS )
- sqlite3_free(ContainerID);
- free(orderBy);
- free(newSearchCriteria);
- free(str.data);
+ bodylen = snprintf(body, sizeof(body), resp,
+ action, "urn:schemas-upnp-org:service:WANIPConnection:1",
+ ext_ip_addr, action);
+ BuildSendAndCloseSoapResp(h, body, bodylen);
}
/*
@@ -1376,7 +642,7 @@ QueryStateVariable(struct upnphttp * h, const char * action)
/*var_name = GetValueFromNameValueListIgnoreNS(&data, "varName");*/
var_name = GetValueFromNameValueList(&data, "varName");
- DPRINTF(E_INFO, L_HTTP, "QueryStateVariable(%.40s)\n", var_name);
+ /*syslog(LOG_INFO, "QueryStateVariable(%.40s)", var_name); */
if(!var_name)
{
@@ -1389,35 +655,28 @@ QueryStateVariable(struct upnphttp * h, const char * action)
"Connected", action);
BuildSendAndCloseSoapResp(h, body, bodylen);
}
+#if 0
+ /* not usefull */
+ else if(strcmp(var_name, "ConnectionType") == 0)
+ {
+ bodylen = snprintf(body, sizeof(body), resp, "IP_Routed");
+ BuildSendAndCloseSoapResp(h, body, bodylen);
+ }
+ else if(strcmp(var_name, "LastConnectionError") == 0)
+ {
+ bodylen = snprintf(body, sizeof(body), resp, "ERROR_NONE");
+ BuildSendAndCloseSoapResp(h, body, bodylen);
+ }
+#endif
else
{
- DPRINTF(E_WARN, L_HTTP, "%s: Unknown: %s\n", action, var_name?var_name:"");
+ syslog(LOG_NOTICE, "%s: Unknown: %s", action, var_name?var_name:"");
SoapError(h, 404, "Invalid Var");
}
ClearNameValueList(&data);
}
-static void
-SamsungGetFeatureList(struct upnphttp * h, const char * action)
-{
- static const char resp[] =
- "<u:X_GetFeatureListResponse xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">"
- "<FeatureList>"
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
- "&lt;Features xmlns=\"urn:schemas-upnp-org:av:avs\""
- " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
- " xsi:schemaLocation=\"urn:schemas-upnp-org:av:avs http://www.upnp.org/schemas/av/avs.xsd\"&gt;"
- "&lt;Feature name=\"samsung.com_BASICVIEW\" version=\"1\"&gt;"
- "&lt;container id=\"1\" type=\"object.item.audioItem\"/&gt;"
- "&lt;container id=\"2\" type=\"object.item.videoItem\"/&gt;"
- "&lt;container id=\"3\" type=\"object.item.imageItem\"/&gt;"
- "&lt;/Feature&gt;"
- "</FeatureList></u:X_GetFeatureListResponse>";
-
- BuildSendAndCloseSoapResp(h, resp, sizeof(resp));
-}
-
static const struct
{
const char * methodName;
@@ -1425,7 +684,9 @@ static const struct
}
soapMethods[] =
{
+ { "GetExternalIPAddress", GetExternalIPAddress},
{ "QueryStateVariable", QueryStateVariable},
+ { "GetStatusInfo", GetStatusInfo},
{ "Browse", BrowseContentDirectory},
{ "Search", SearchContentDirectory},
{ "GetSearchCapabilities", GetSearchCapabilities},
@@ -1436,7 +697,6 @@ soapMethods[] =
{ "GetCurrentConnectionInfo", GetCurrentConnectionInfo},
{ "IsAuthorized", IsAuthorizedValidated},
{ "IsValidated", IsAuthorizedValidated},
- { "X_GetFeatureList", SamsungGetFeatureList},
{ 0, 0 }
};
@@ -1444,21 +704,21 @@ void
ExecuteSoapAction(struct upnphttp * h, const char * action, int n)
{
char * p;
+ char * p2;
+ int i, len, methodlen;
+ i = 0;
p = strchr(action, '#');
+
if(p)
{
- int i = 0;
- int len;
- int methodlen;
- char * p2;
p++;
p2 = strchr(p, '"');
if(p2)
methodlen = p2 - p;
else
methodlen = n - (p - action);
- DPRINTF(E_DEBUG, L_HTTP, "SoapMethod: %.*s\n", methodlen, p);
+ /*syslog(LOG_DEBUG, "SoapMethod: %.*s", methodlen, p);*/
while(soapMethods[i].methodName)
{
len = strlen(soapMethods[i].methodName);
@@ -1470,7 +730,7 @@ ExecuteSoapAction(struct upnphttp * h, const char * action, int n)
i++;
}
- DPRINTF(E_WARN, L_HTTP, "SoapMethod: Unknown: %.*s\n", methodlen, p);
+ syslog(LOG_NOTICE, "SoapMethod: Unknown: %.*s", methodlen, p);
}
SoapError(h, 401, "Invalid Action");
@@ -1518,7 +778,7 @@ SoapError(struct upnphttp * h, int errCode, const char * errDesc)
char body[2048];
int bodylen;
- DPRINTF(E_WARN, L_HTTP, "Returning UPnPError %d: %s\n", errCode, errDesc);
+ syslog(LOG_INFO, "Returning UPnPError %d: %s", errCode, errDesc);
bodylen = snprintf(body, sizeof(body), resp, errCode, errDesc);
BuildResp2_upnphttp(h, 500, "Internal Server Error", body, bodylen);
SendResp_upnphttp(h);
diff --git a/upnpsoap.h b/upnpsoap.h
index 9f75c03..ae5b031 100644
--- a/upnpsoap.h
+++ b/upnpsoap.h
@@ -1,47 +1,12 @@
-/* MiniDLNA project
- * http://minidlna.sourceforge.net/
- *
- * MiniDLNA media server
- * Copyright (C) 2008-2009 Justin Maggard
- *
- * This file is part of MiniDLNA.
- *
- * MiniDLNA is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * MiniDLNA 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 MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
- */
+/* MiniUPnP project
+ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+ * (c) 2006 Thomas Bernard
+ * This software is subject to the conditions detailed
+ * in the LICENCE file provided within the distribution */
+
#ifndef __UPNPSOAP_H__
#define __UPNPSOAP_H__
-#define DEFAULT_RESP_SIZE 131072
-#define MAX_RESPONSE_SIZE 2097152
-
-#define CONTENT_DIRECTORY_SCHEMAS \
- " xmlns:dc=\"http://purl.org/dc/elements/1.1/\"" \
- " xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\"" \
- " xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\""
-#define DLNA_NAMESPACE \
- " xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\""
-
-struct Response
-{
- struct string_s *str;
- int start;
- int returned;
- int requested;
- uint32_t filter;
- uint32_t flags;
- enum client_types client;
-};
-
/* ExecuteSoapAction():
* this method executes the requested Soap Action */
void