Initial checkin
This commit is contained in:
433
Changelog.txt
Normal file
433
Changelog.txt
Normal file
@ -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
|
||||||
|
|
14
INSTALL
Normal file
14
INSTALL
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
MiniDLNA project.
|
||||||
|
(c) 2008 Justin Maggard
|
||||||
|
Parts (c) 2006-2008 Thomas Bernard
|
||||||
|
Homepage : http://sourceforge.net/projects/minidlna/
|
||||||
|
|
||||||
|
Prerequisites :
|
||||||
|
|
||||||
|
- taglib
|
||||||
|
- libexif
|
||||||
|
- sqlite3
|
||||||
|
|
||||||
|
To Build and install :
|
||||||
|
|
||||||
|
- Just run make, and hope it works. :)
|
346
LICENCE
Normal file
346
LICENCE
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
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 BusyBox (or modified versions derived from this one) may be
|
||||||
|
distributed under.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Library General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Library General
|
||||||
|
Public License instead of this License.
|
26
LICENCE.miniupnpd
Normal file
26
LICENCE.miniupnpd
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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.
|
||||||
|
|
101
Makefile
Normal file
101
Makefile
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# $Id$
|
||||||
|
# MiniUPnP project
|
||||||
|
# http://miniupnp.free.fr/
|
||||||
|
# Author : Thomas Bernard
|
||||||
|
# for use with GNU Make
|
||||||
|
# To install use :
|
||||||
|
# $ PREFIX=/dummyinstalldir make -f Makefile.linux install
|
||||||
|
# or :
|
||||||
|
# $ INSTALLPREFIX=/usr/local make -f Makefile.linux install
|
||||||
|
# or :
|
||||||
|
# $ make -f Makefile.linux install
|
||||||
|
#
|
||||||
|
#CFLAGS = -Wall -O -D_GNU_SOURCE -g -DDEBUG
|
||||||
|
#CFLAGS = -Wall -g -Os -D_GNU_SOURCE
|
||||||
|
CFLAGS = -Wall -g -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
|
||||||
|
CC = gcc
|
||||||
|
RM = rm -f
|
||||||
|
INSTALL = install
|
||||||
|
|
||||||
|
INSTALLPREFIX ?= $(PREFIX)/usr
|
||||||
|
SBININSTALLDIR = $(INSTALLPREFIX)/sbin
|
||||||
|
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 upnpevents.o \
|
||||||
|
sql.o metadata.o scanner.o
|
||||||
|
|
||||||
|
ALLOBJS = $(BASEOBJS) $(LNXOBJS)
|
||||||
|
|
||||||
|
#LIBS = -liptc
|
||||||
|
LIBS = -lexif -ltag_c -lsqlite3 #-lgd
|
||||||
|
|
||||||
|
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
|
||||||
|
|
||||||
|
EXECUTABLES = minidlna testupnpdescgen
|
||||||
|
|
||||||
|
.PHONY: all clean install depend genuuid
|
||||||
|
|
||||||
|
all: $(EXECUTABLES)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) $(ALLOBJS)
|
||||||
|
$(RM) $(EXECUTABLES)
|
||||||
|
$(RM) testupnpdescgen.o
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
config.h: genconfig.sh
|
||||||
|
./genconfig.sh
|
||||||
|
|
||||||
|
depend: config.h
|
||||||
|
makedepend -f$(MAKEFILE_LIST) -Y \
|
||||||
|
$(ALLOBJS:.o=.c) $(TESTUPNPDESCGENOBJS:.o=.c) 2>/dev/null
|
||||||
|
|
||||||
|
# DO NOT DELETE
|
||||||
|
|
||||||
|
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
|
||||||
|
daemonize.o: daemonize.h config.h
|
||||||
|
upnpglobalvars.o: config.h upnpglobalvars.h
|
||||||
|
upnpglobalvars.o: miniupnpdtypes.h
|
||||||
|
options.o: options.h config.h upnpglobalvars.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 miniupnpdpath.h upnpglobalvars.h
|
||||||
|
upnpdescgen.o: miniupnpdtypes.h upnpdescstrings.h
|
25
README
Normal file
25
README
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
MiniDLNA project
|
||||||
|
(c) 2008 Justin Maggard
|
||||||
|
Parts (c) 2006-2007 Thomas Bernard
|
||||||
|
webpage: http://sourceforge.net/projects/minidlna/
|
||||||
|
|
||||||
|
This directory contain the MiniDLNA daemon software.
|
||||||
|
This software is subject to the conditions detailed in
|
||||||
|
the LICENCE file provided with this distribution.
|
||||||
|
|
||||||
|
Parts of the software including the discovery code are
|
||||||
|
licensed under the BSD revised license which is detailed
|
||||||
|
in the LICENSE.miniupnpd file provided with the distribution.
|
||||||
|
More information on MiniUPnPd can be found at http://miniupnp.free.fr.
|
||||||
|
|
||||||
|
|
||||||
|
The MiniDLNA daemon is an UPnP-A/V and DLNA service which
|
||||||
|
serves multimedia content to compatible clients on the network.
|
||||||
|
See http://www.upnp.org/ for more details on UPnP
|
||||||
|
and http://www.dlna.org/ for mode details on DLNA.
|
||||||
|
|
||||||
|
See the INSTALL file for instructions on compiling, installing,
|
||||||
|
and configuring minidlna.
|
||||||
|
|
||||||
|
|
||||||
|
Justin Maggard
|
35
commonrdr.h
Normal file
35
commonrdr.h
Normal file
@ -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
|
||||||
|
|
55
config.h
Normal file
55
config.h
Normal file
@ -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
|
129
daemonize.c
Normal file
129
daemonize.c
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/* $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 <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "daemonize.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifndef USE_DAEMON
|
||||||
|
|
||||||
|
int
|
||||||
|
daemonize(void)
|
||||||
|
{
|
||||||
|
int pid, i;
|
||||||
|
|
||||||
|
switch(fork())
|
||||||
|
{
|
||||||
|
/* fork error */
|
||||||
|
case -1:
|
||||||
|
perror("fork()");
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
/* child process */
|
||||||
|
case 0:
|
||||||
|
/* obtain a new process group */
|
||||||
|
if( (pid = setsid()) < 0)
|
||||||
|
{
|
||||||
|
perror("setsid()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* close all descriptors */
|
||||||
|
for (i=getdtablesize();i>=0;--i) close(i);
|
||||||
|
|
||||||
|
i = open("/dev/null",O_RDWR); /* open stdin */
|
||||||
|
dup(i); /* stdout */
|
||||||
|
dup(i); /* stderr */
|
||||||
|
|
||||||
|
umask(027);
|
||||||
|
chdir("/"); /* chdir to /tmp ? */
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
|
||||||
|
/* parent process */
|
||||||
|
default:
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
writepidfile(const char * fname, int pid)
|
||||||
|
{
|
||||||
|
char pidstring[16];
|
||||||
|
int pidstringlen;
|
||||||
|
int pidfile;
|
||||||
|
|
||||||
|
if(!fname || (strlen(fname) == 0))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if( (pidfile = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR,
|
||||||
|
"Unable to write to pidfile %s: snprintf(): FAILED", fname);
|
||||||
|
close(pidfile);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(write(pidfile, pidstring, pidstringlen) < 0)
|
||||||
|
syslog(LOG_ERR, "Unable to write to pidfile %s: %m", fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(pidfile);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
checkforrunning(const char * fname)
|
||||||
|
{
|
||||||
|
char buffer[64];
|
||||||
|
int pidfile;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
if(!fname || (strlen(fname) == 0))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if( (pidfile = open(fname, O_RDONLY)) < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(buffer, 0, 64);
|
||||||
|
|
||||||
|
if(read(pidfile, buffer, 63))
|
||||||
|
{
|
||||||
|
if( (pid = atol(buffer)) > 0)
|
||||||
|
{
|
||||||
|
if(!kill(pid, 0))
|
||||||
|
{
|
||||||
|
close(pidfile);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(pidfile);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
34
daemonize.h
Normal file
34
daemonize.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* 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 __DAEMONIZE_H__
|
||||||
|
#define __DAEMONIZE_H__
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifndef USE_DAEMON
|
||||||
|
/* daemonize()
|
||||||
|
* "fork" to background, detach from terminal, etc...
|
||||||
|
* returns: pid of the daemon, exits upon failure */
|
||||||
|
int
|
||||||
|
daemonize(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* writepidfile()
|
||||||
|
* write the pid to a file */
|
||||||
|
int
|
||||||
|
writepidfile(const char * fname, int pid);
|
||||||
|
|
||||||
|
/* checkforrunning()
|
||||||
|
* check for another instance running
|
||||||
|
* returns: 0 only instance
|
||||||
|
* -1 invalid filename
|
||||||
|
* -2 another instance running */
|
||||||
|
int
|
||||||
|
checkforrunning(const char * fname);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
179
genconfig.sh
Executable file
179
genconfig.sh
Executable file
@ -0,0 +1,179 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
# $Id$
|
||||||
|
# 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__"
|
||||||
|
|
||||||
|
# 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`
|
||||||
|
|
||||||
|
# pfSense special case
|
||||||
|
if [ -f /etc/platform ]; then
|
||||||
|
if [ `cat /etc/platform` = "pfSense" ]; then
|
||||||
|
OS_NAME=pfSense
|
||||||
|
OS_VERSION=`cat /etc/version`
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
${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
|
||||||
|
OpenBSD)
|
||||||
|
MAJORVER=`echo $OS_VERSION | cut -d. -f1`
|
||||||
|
MINORVER=`echo $OS_VERSION | cut -d. -f2`
|
||||||
|
#echo "OpenBSD majorversion=$MAJORVER minorversion=$MINORVER"
|
||||||
|
# rtableid was introduced in OpenBSD 4.0
|
||||||
|
if [ $MAJORVER -ge 4 ]; then
|
||||||
|
echo "#define PFRULE_HAS_RTABLEID" >> ${CONFIGFILE}
|
||||||
|
fi
|
||||||
|
# from the 3.8 version, packets and bytes counters are double : in/out
|
||||||
|
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)
|
||||||
|
VER=`grep '#define __FreeBSD_version' /usr/include/sys/param.h | awk '{print $3}'`
|
||||||
|
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}
|
||||||
|
echo "#define LOG_PERROR 0" >> ${CONFIGFILE}
|
||||||
|
echo "#define SOLARIS_KSTATS 1" >> ${CONFIGFILE}
|
||||||
|
echo "typedef uint64_t u_int64_t;" >> ${CONFIGFILE}
|
||||||
|
echo "typedef uint32_t u_int32_t;" >> ${CONFIGFILE}
|
||||||
|
echo "typedef uint16_t u_int16_t;" >> ${CONFIGFILE}
|
||||||
|
echo "typedef uint8_t u_int8_t;" >> ${CONFIGFILE}
|
||||||
|
OS_URL=http://www.sun.com/solaris/
|
||||||
|
;;
|
||||||
|
Linux)
|
||||||
|
OS_URL=http://www.kernel.org/
|
||||||
|
KERNVERA=`echo $OS_VERSION | awk -F. '{print $1}'`
|
||||||
|
KERNVERB=`echo $OS_VERSION | awk -F. '{print $2}'`
|
||||||
|
KERNVERC=`echo $OS_VERSION | awk -F. '{print $3}'`
|
||||||
|
KERNVERD=`echo $OS_VERSION | awk -F. '{print $4}'`
|
||||||
|
#echo "$KERNVERA.$KERNVERB.$KERNVERC.$KERNVERD"
|
||||||
|
# Debian GNU/Linux special case
|
||||||
|
if [ -f /etc/debian_version ]; then
|
||||||
|
OS_NAME=Debian
|
||||||
|
OS_VERSION=`cat /etc/debian_version`
|
||||||
|
OS_URL=http://www.debian.org/
|
||||||
|
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 "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
|
echo "/* syslog facility to be used by miniupnpd */" >> ${CONFIGFILE}
|
||||||
|
echo "#define LOG_MINIUPNPD ${LOG_MINIUPNPD}" >> ${CONFIGFILE}
|
||||||
|
echo "" >> ${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 "/* 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}
|
||||||
|
echo " * of BSD daemon() */" >> ${CONFIGFILE}
|
||||||
|
echo "#define USE_DAEMON" >> ${CONFIGFILE}
|
||||||
|
echo "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
|
echo "/* Uncomment the following line to enable lease file support */" >> ${CONFIGFILE}
|
||||||
|
echo "/*#define ENABLE_LEASEFILE*/" >> ${CONFIGFILE}
|
||||||
|
echo "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
|
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 "/* Experimental UPnP Events support. */" >> ${CONFIGFILE}
|
||||||
|
echo "/*#define ENABLE_EVENTS*/" >> ${CONFIGFILE}
|
||||||
|
echo "" >> ${CONFIGFILE}
|
||||||
|
|
||||||
|
echo "#endif" >> ${CONFIGFILE}
|
||||||
|
|
||||||
|
exit 0
|
55
getifaddr.c
Normal file
55
getifaddr.c
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* $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 <string.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#if defined(sun)
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "getifaddr.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
getifaddr(const char * ifname, char * buf, int len)
|
||||||
|
{
|
||||||
|
/* SIOCGIFADDR struct ifreq * */
|
||||||
|
int s;
|
||||||
|
struct ifreq ifr;
|
||||||
|
int ifrlen;
|
||||||
|
struct sockaddr_in * addr;
|
||||||
|
ifrlen = sizeof(ifr);
|
||||||
|
s = socket(PF_INET, SOCK_DGRAM, 0);
|
||||||
|
if(s < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "socket(PF_INET, SOCK_DGRAM): %m");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
|
||||||
|
if(ioctl(s, SIOCGIFADDR, &ifr, &ifrlen) < 0)
|
||||||
|
{
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "inet_ntop(): %m");
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
18
getifaddr.h
Normal file
18
getifaddr.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* 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 __GETIFADDR_H__
|
||||||
|
#define __GETIFADDR_H__
|
||||||
|
|
||||||
|
/* getifaddr()
|
||||||
|
* take a network interface name and write the
|
||||||
|
* ip v4 address as text in the buffer
|
||||||
|
* returns: 0 success, -1 failure */
|
||||||
|
int
|
||||||
|
getifaddr(const char * ifname, char * buf, int len);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
40
linux/miniupnpd.init.d.script
Normal file
40
linux/miniupnpd.init.d.script
Normal file
@ -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
|
406
metadata.c
Normal file
406
metadata.c
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
/* MiniDLNA media server
|
||||||
|
* Copyright (C) 2008 Justin Maggard
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
#include <taglib/tag_c.h>
|
||||||
|
#include <libexif/exif-loader.h>
|
||||||
|
|
||||||
|
#include "upnpglobalvars.h"
|
||||||
|
#include "metadata.h"
|
||||||
|
|
||||||
|
#define FLAG_ARTIST 0x01
|
||||||
|
|
||||||
|
char *
|
||||||
|
trim(char *str)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
modifyString(char * string, const char * before, const char * after, short like)
|
||||||
|
{
|
||||||
|
int oldlen, newlen, chgcnt = 0;
|
||||||
|
char *s, *p, *t;
|
||||||
|
|
||||||
|
oldlen = strlen(before);
|
||||||
|
newlen = strlen(after);
|
||||||
|
if( newlen > oldlen )
|
||||||
|
{
|
||||||
|
s = string;
|
||||||
|
while( (p = strstr(s, before)) )
|
||||||
|
{
|
||||||
|
chgcnt++;
|
||||||
|
s = p+oldlen;
|
||||||
|
}
|
||||||
|
string = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
s = string;
|
||||||
|
while( s )
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
strip_ext(char * name)
|
||||||
|
{
|
||||||
|
if( rindex(name, '.') )
|
||||||
|
*rindex(name, '.') = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite_int64
|
||||||
|
GetAudioMetadata(const char * path, char * name)
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
char date[16], duration[16], dlna_pn[24], mime[16];
|
||||||
|
struct stat file;
|
||||||
|
int seconds, minutes;
|
||||||
|
sqlite_int64 ret;
|
||||||
|
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);
|
||||||
|
|
||||||
|
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) )
|
||||||
|
{
|
||||||
|
title = trim(title);
|
||||||
|
if( index(title, '&') )
|
||||||
|
{
|
||||||
|
title = modifyString(strdup(title), "&", "&amp;", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
title = name;
|
||||||
|
}
|
||||||
|
artist = taglib_tag_artist(tag);
|
||||||
|
if( strlen(artist) )
|
||||||
|
{
|
||||||
|
artist = trim(artist);
|
||||||
|
if( index(artist, '&') )
|
||||||
|
{
|
||||||
|
free_flags |= FLAG_ARTIST;
|
||||||
|
artist = modifyString(strdup(artist), "&", "&amp;", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
artist = NULL;
|
||||||
|
}
|
||||||
|
album = taglib_tag_album(tag);
|
||||||
|
if( strlen(album) )
|
||||||
|
{
|
||||||
|
album = trim(album);
|
||||||
|
if( index(album, '&') )
|
||||||
|
{
|
||||||
|
album = modifyString(strdup(album), "&", "&amp;", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
album = NULL;
|
||||||
|
}
|
||||||
|
genre = taglib_tag_genre(tag);
|
||||||
|
if( strlen(genre) )
|
||||||
|
{
|
||||||
|
genre = trim(genre);
|
||||||
|
if( index(genre, '&') )
|
||||||
|
{
|
||||||
|
genre = modifyString(strdup(genre), "&", "&amp;", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
genre = NULL;
|
||||||
|
}
|
||||||
|
comment = taglib_tag_comment(tag);
|
||||||
|
if( strlen(comment) )
|
||||||
|
{
|
||||||
|
comment = trim(comment);
|
||||||
|
if( index(comment, '&') )
|
||||||
|
{
|
||||||
|
comment = modifyString(strdup(comment), "&", "&amp;", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
comment = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( 1 ) // Switch on audio file type
|
||||||
|
{
|
||||||
|
strcpy(dlna_pn, "MP3;DLNA.ORG_OP=01");
|
||||||
|
strcpy(mime, "audio/mpeg");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
sqlite3_free(sql);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite_int64
|
||||||
|
GetImageMetadata(const char * path, char * name)
|
||||||
|
{
|
||||||
|
ExifData *ed;
|
||||||
|
ExifEntry *e = NULL;
|
||||||
|
ExifTag tag;
|
||||||
|
int width=0, height=0, thumb=0;
|
||||||
|
size_t size;
|
||||||
|
char date[64], make[32], model[64], dlna_pn[64];
|
||||||
|
char b[1024];
|
||||||
|
struct stat file;
|
||||||
|
sqlite_int64 ret;
|
||||||
|
char *sql;
|
||||||
|
char *zErrMsg = NULL;
|
||||||
|
|
||||||
|
date[0] = '\0';
|
||||||
|
model[0] = '\0';
|
||||||
|
dlna_pn[0] = '\0';
|
||||||
|
|
||||||
|
//DEBUG printf("Parsing %s...\n", path);
|
||||||
|
if ( stat(path, &file) == 0 )
|
||||||
|
size = file.st_size;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
strip_ext(name);
|
||||||
|
//DEBUG printf(" * size: %d\n", size);
|
||||||
|
|
||||||
|
ExifLoader * l = exif_loader_new();
|
||||||
|
exif_loader_write_file(l, path);
|
||||||
|
ed = exif_loader_get_data(l);
|
||||||
|
exif_loader_unref(l);
|
||||||
|
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
date[4] = '-';
|
||||||
|
date[7] = '-';
|
||||||
|
date[10] = 'T';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
strcpy(date, "0000-00-00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
strcpy(date, "0000-00-00");
|
||||||
|
}
|
||||||
|
//DEBUG printf(" * date: %s\n", date);
|
||||||
|
|
||||||
|
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));
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( !strlen(model) )
|
||||||
|
strcpy(model, "Unknown");
|
||||||
|
//DEBUG printf(" * model: %s\n", model);
|
||||||
|
|
||||||
|
if( ed->size )
|
||||||
|
thumb = 1;
|
||||||
|
else
|
||||||
|
thumb = 0;
|
||||||
|
//DEBUG printf(" * thumbnail: %d\n", thumb);
|
||||||
|
|
||||||
|
exif_data_unref(ed);
|
||||||
|
|
||||||
|
if( width <= 640 && height <= 480 )
|
||||||
|
strcpy(dlna_pn, "JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
|
||||||
|
else if( width <= 1024 && height <= 768 )
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
sqlite3_free(sql);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite_int64
|
||||||
|
GetVideoMetadata(const char * path, char * name)
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
struct stat file;
|
||||||
|
char *sql;
|
||||||
|
char *zErrMsg = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
//DEBUG printf("Parsing %s...\n", path);
|
||||||
|
if ( stat(path, &file) == 0 )
|
||||||
|
size = file.st_size;
|
||||||
|
strip_ext(name);
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
sqlite3_free(sql);
|
||||||
|
return ret;
|
||||||
|
}
|
25
metadata.h
Normal file
25
metadata.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* Metadata extraction
|
||||||
|
*
|
||||||
|
* Project : minidlna
|
||||||
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
|
* Author : Justin Maggard
|
||||||
|
* 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__
|
||||||
|
|
||||||
|
char *
|
||||||
|
modifyString(char * string, const char * before, const char * after, short like);
|
||||||
|
|
||||||
|
sqlite_int64
|
||||||
|
GetAudioMetadata(const char * path, char * name);
|
||||||
|
|
||||||
|
sqlite_int64
|
||||||
|
GetImageMetadata(const char * path, char * name);
|
||||||
|
|
||||||
|
sqlite_int64
|
||||||
|
GetVideoMetadata(const char * path, char * name);
|
||||||
|
|
||||||
|
#endif
|
872
minidlna.c
Normal file
872
minidlna.c
Normal file
@ -0,0 +1,872 @@
|
|||||||
|
/* 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
|
||||||
|
*
|
||||||
|
* Portions of the code (c) Thomas Bernard, subject to
|
||||||
|
* the conditions detailed in the LICENSE.miniupnpd file.
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#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>
|
||||||
|
#if defined(sun)
|
||||||
|
#include <kstat.h>
|
||||||
|
#else
|
||||||
|
/* for BSD's sysctl */
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
/* unix sockets */
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "upnpglobalvars.h"
|
||||||
|
#include "upnphttp.h"
|
||||||
|
#include "upnpdescgen.h"
|
||||||
|
#include "miniupnpdpath.h"
|
||||||
|
#include "getifaddr.h"
|
||||||
|
#include "upnpsoap.h"
|
||||||
|
#include "options.h"
|
||||||
|
#include "minissdp.h"
|
||||||
|
#include "miniupnpdtypes.h"
|
||||||
|
#include "daemonize.h"
|
||||||
|
#include "upnpevents.h"
|
||||||
|
#include "scanner.h"
|
||||||
|
#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;
|
||||||
|
|
||||||
|
/* OpenAndConfHTTPSocket() :
|
||||||
|
* setup the socket used to handle incoming HTTP connections. */
|
||||||
|
static int
|
||||||
|
OpenAndConfHTTPSocket(unsigned short port)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
int i = 1;
|
||||||
|
struct sockaddr_in listenname;
|
||||||
|
|
||||||
|
if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "socket(http): %m");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_WARNING, "setsockopt(http, SO_REUSEADDR): %m");
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&listenname, 0, sizeof(struct sockaddr_in));
|
||||||
|
listenname.sin_family = AF_INET;
|
||||||
|
listenname.sin_port = htons(port);
|
||||||
|
listenname.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
|
||||||
|
if(bind(s, (struct sockaddr *)&listenname, sizeof(struct sockaddr_in)) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "bind(http): %m");
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(listen(s, 6) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "listen(http): %m");
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handler for the SIGTERM signal (kill)
|
||||||
|
* SIGINT is also handled */
|
||||||
|
static void
|
||||||
|
sigterm(int sig)
|
||||||
|
{
|
||||||
|
/*int save_errno = errno;*/
|
||||||
|
signal(sig, SIG_IGN); /* Ignore this signal while we are quitting */
|
||||||
|
|
||||||
|
syslog(LOG_NOTICE, "received signal %d, good-bye", sig);
|
||||||
|
|
||||||
|
quitting = 1;
|
||||||
|
/*errno = save_errno;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* record the startup time, for returning uptime */
|
||||||
|
static 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
|
||||||
|
* return value :
|
||||||
|
* 0 : ok
|
||||||
|
* -1 : error */
|
||||||
|
static int
|
||||||
|
parselanaddr(struct lan_addr_s * lan_addr, const char * str)
|
||||||
|
{
|
||||||
|
const char * p;
|
||||||
|
int nbits = 24;
|
||||||
|
int n;
|
||||||
|
p = str;
|
||||||
|
while(*p && *p != '/' && !isspace(*p))
|
||||||
|
p++;
|
||||||
|
n = p - str;
|
||||||
|
if(*p == '/')
|
||||||
|
{
|
||||||
|
nbits = atoi(++p);
|
||||||
|
while(*p && !isspace(*p))
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if(n>15)
|
||||||
|
{
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error parsing address/mask : %s\n", str);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
lan_addr->mask.s_addr = htonl(nbits ? (0xffffffff << (32 - nbits)) : 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* init phase :
|
||||||
|
* 1) read configuration file
|
||||||
|
* 2) read command line arguments
|
||||||
|
* 3) daemonize
|
||||||
|
* 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, 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";
|
||||||
|
|
||||||
|
/* first check if "-f" option is used */
|
||||||
|
for(i=2; i<argc; i++)
|
||||||
|
{
|
||||||
|
if(0 == strcmp(argv[i-1], "-f"))
|
||||||
|
{
|
||||||
|
optionsfile = argv[i];
|
||||||
|
options_flag = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*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) )
|
||||||
|
{
|
||||||
|
printf("No IP!\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
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 */
|
||||||
|
if(readoptionsfile(optionsfile) < 0)
|
||||||
|
{
|
||||||
|
/* only error if file exists or using -f */
|
||||||
|
if(access(optionsfile, F_OK) == 0 || options_flag)
|
||||||
|
fprintf(stderr, "Error reading configuration file %s\n", optionsfile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(i=0; i<num_options; i++)
|
||||||
|
{
|
||||||
|
switch(ary_options[i].id)
|
||||||
|
{
|
||||||
|
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(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++; /*v->n_lan_addr++; */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n",
|
||||||
|
MAX_LAN_ADDR, ary_options[i].value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UPNPPORT:
|
||||||
|
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:
|
||||||
|
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_NUMBER:
|
||||||
|
strncpy(modelnumber, ary_options[i].value, MODELNUMBER_MAX_LEN);
|
||||||
|
modelnumber[MODELNUMBER_MAX_LEN-1] = '\0';
|
||||||
|
break;
|
||||||
|
case UPNPCLEANTHRESHOLD:
|
||||||
|
v->clean_ruleset_threshold = atoi(ary_options[i].value);
|
||||||
|
break;
|
||||||
|
case UPNPCLEANINTERVAL:
|
||||||
|
v->clean_ruleset_interval = atoi(ary_options[i].value);
|
||||||
|
break;
|
||||||
|
#ifdef USE_PF
|
||||||
|
case UPNPQUEUE:
|
||||||
|
queue = ary_options[i].value;
|
||||||
|
break;
|
||||||
|
case UPNPTAG:
|
||||||
|
tag = ary_options[i].value;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef PF_ENABLE_FILTER_RULES
|
||||||
|
case UPNPQUICKRULES:
|
||||||
|
if(strcmp(ary_options[i].value, "no") == 0)
|
||||||
|
SETFLAG(PFNOQUICKRULESMASK);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case UPNPSECUREMODE:
|
||||||
|
if(strcmp(ary_options[i].value, "yes") == 0)
|
||||||
|
SETFLAG(SECUREMODEMASK);
|
||||||
|
break;
|
||||||
|
#ifdef ENABLE_LEASEFILE
|
||||||
|
case UPNPLEASEFILE:
|
||||||
|
lease_file = ary_options[i].value;
|
||||||
|
remove(lease_file);
|
||||||
|
break;
|
||||||
|
#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",
|
||||||
|
optionsfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* command line arguments processing */
|
||||||
|
for(i=1; i<argc; i++)
|
||||||
|
{
|
||||||
|
if(argv[i][0]!='-')
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
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;
|
||||||
|
case 's':
|
||||||
|
if(i+1 < argc)
|
||||||
|
strncpy(serialnumber, argv[++i], SERIALNUMBER_MAX_LEN);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||||
|
serialnumber[SERIALNUMBER_MAX_LEN-1] = '\0';
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
if(i+1 < argc)
|
||||||
|
strncpy(modelnumber, argv[++i], MODELNUMBER_MAX_LEN);
|
||||||
|
else
|
||||||
|
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)
|
||||||
|
v->port = atoi(argv[++i]);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
if(i+1 < argc)
|
||||||
|
pidfilename = argv[++i];
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
debug_flag = 1;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
if(i+1 < argc)
|
||||||
|
presurl = argv[++i];
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
if(i+2<argc)
|
||||||
|
{
|
||||||
|
downstream_bitrate = strtoul(argv[++i], 0, 0);
|
||||||
|
upstream_bitrate = strtoul(argv[++i], 0, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Option -%c takes two arguments.\n", argv[i][1]);
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
if(i+1 < argc)
|
||||||
|
{
|
||||||
|
int address_already_there = 0;
|
||||||
|
int j;
|
||||||
|
i++;
|
||||||
|
for(j=0; j<n_lan_addr; j++)/* for(j=0; j<v->n_lan_addr; j++)*/
|
||||||
|
{
|
||||||
|
struct lan_addr_s tmpaddr;
|
||||||
|
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(v->n_lan_addr < MAX_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
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n",
|
||||||
|
MAX_LAN_ADDR, argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
i++; /* discarding, the config file is already read */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!ext_if_name || (/*v->*/n_lan_addr==0) || v->port<=0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage:\n\t"
|
||||||
|
"%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();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef USE_DAEMON
|
||||||
|
if(daemon(0, 0)<0) {
|
||||||
|
perror("daemon()");
|
||||||
|
}
|
||||||
|
pid = getpid();
|
||||||
|
#else
|
||||||
|
pid = daemonize();
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "MiniUPnPd is already running. EXITING");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_startup_time(GETFLAG(SYSUPTIMEMASK)/*sysuptime*/);
|
||||||
|
|
||||||
|
/* presentation url */
|
||||||
|
if(presurl)
|
||||||
|
{
|
||||||
|
strncpy(presentationurl, presurl, PRESENTATIONURL_MAX_LEN);
|
||||||
|
presentationurl[PRESENTATIONURL_MAX_LEN-1] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(presentationurl, PRESENTATIONURL_MAX_LEN,
|
||||||
|
"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 */
|
||||||
|
memset(&sa, 0, sizeof(struct sigaction));
|
||||||
|
sa.sa_handler = sigterm;
|
||||||
|
|
||||||
|
if (sigaction(SIGTERM, &sa, NULL))
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGTERM");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (sigaction(SIGINT, &sa, NULL))
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "Failed to set %s handler. EXITING", "SIGINT");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
||||||
|
syslog(LOG_ERR, "Failed to ignore SIGPIPE signals");
|
||||||
|
}
|
||||||
|
|
||||||
|
writepidfile(pidfilename, pid);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === main === */
|
||||||
|
/* process HTTP or SSDP requests */
|
||||||
|
int
|
||||||
|
main(int argc, char * * argv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int sudp = -1, shttpl = -1;
|
||||||
|
int snotify[MAX_LAN_ADDR];
|
||||||
|
LIST_HEAD(httplisthead, upnphttp) upnphttphead;
|
||||||
|
struct upnphttp * e = 0;
|
||||||
|
struct upnphttp * next;
|
||||||
|
fd_set readset; /* for select() */
|
||||||
|
#ifdef ENABLE_EVENTS
|
||||||
|
fd_set writeset;
|
||||||
|
#endif
|
||||||
|
struct timeval timeout, timeofday, lasttimeofday = {0, 0};
|
||||||
|
int max_fd = -1;
|
||||||
|
struct runtime_vars v;
|
||||||
|
|
||||||
|
if(init(argc, argv, &v) != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
LIST_INIT(&upnphttphead);
|
||||||
|
|
||||||
|
if( access("/tmp/files.db", F_OK) )
|
||||||
|
{
|
||||||
|
sqlite3_open("/tmp/files.db", &db);
|
||||||
|
ScanDirectory(media_dir, NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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(v.port);
|
||||||
|
if(shttpl < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
syslog(LOG_NOTICE, "HTTP listening on port %d", v.port);
|
||||||
|
|
||||||
|
/* open socket for sending notifications */
|
||||||
|
if(OpenAndConfSSDPNotifySockets(snotify) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "Failed to open sockets for sending SSDP notify "
|
||||||
|
"messages. EXITING");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendSSDPGoodbye(snotify, n_lan_addr);
|
||||||
|
|
||||||
|
/* main loop */
|
||||||
|
while(!quitting)
|
||||||
|
{
|
||||||
|
/* Check if we need to send SSDP NOTIFY messages and do it if
|
||||||
|
* needed */
|
||||||
|
if(gettimeofday(&timeofday, 0) < 0)
|
||||||
|
{
|
||||||
|
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 >= (lasttimeofday.tv_sec + v.notify_interval))
|
||||||
|
{
|
||||||
|
SendSSDPNotifies2(snotify,
|
||||||
|
(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 = lasttimeofday.tv_sec + v.notify_interval
|
||||||
|
- timeofday.tv_sec;
|
||||||
|
if(timeofday.tv_usec > lasttimeofday.tv_usec)
|
||||||
|
{
|
||||||
|
timeout.tv_usec = 1000000 + lasttimeofday.tv_usec
|
||||||
|
- timeofday.tv_usec;
|
||||||
|
timeout.tv_sec--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timeout.tv_usec = lasttimeofday.tv_usec - timeofday.tv_usec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
|
||||||
|
FD_ZERO(&readset);
|
||||||
|
|
||||||
|
if (sudp >= 0)
|
||||||
|
{
|
||||||
|
FD_SET(sudp, &readset);
|
||||||
|
max_fd = MAX( max_fd, sudp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shttpl >= 0)
|
||||||
|
{
|
||||||
|
FD_SET(shttpl, &readset);
|
||||||
|
max_fd = MAX( max_fd, shttpl);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* for debug */
|
||||||
|
#ifdef DEBUG
|
||||||
|
if(i > 1)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
/*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)
|
||||||
|
&&(FD_ISSET(e->socket, &readset)) )
|
||||||
|
{
|
||||||
|
Process_upnphttp(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* process incoming HTTP connections */
|
||||||
|
if(shttpl >= 0 && FD_ISSET(shttpl, &readset))
|
||||||
|
{
|
||||||
|
int shttp;
|
||||||
|
socklen_t clientnamelen;
|
||||||
|
struct sockaddr_in clientname;
|
||||||
|
clientnamelen = sizeof(struct sockaddr_in);
|
||||||
|
shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen);
|
||||||
|
if(shttp<0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "accept(http): %m");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct upnphttp * tmp = 0;
|
||||||
|
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) {
|
||||||
|
syslog(LOG_ERR, "fcntl F_SETFL, O_NONBLOCK");
|
||||||
|
}*/
|
||||||
|
/* Create a new upnphttp object and add it to
|
||||||
|
* the active upnphttp object list */
|
||||||
|
tmp = New_upnphttp(shttp);
|
||||||
|
if(tmp)
|
||||||
|
{
|
||||||
|
tmp->clientaddr = clientname.sin_addr;
|
||||||
|
LIST_INSERT_HEAD(&upnphttphead, tmp, entries);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "New_upnphttp() failed");
|
||||||
|
close(shttp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* delete finished HTTP connections */
|
||||||
|
for(e = upnphttphead.lh_first; e != NULL; )
|
||||||
|
{
|
||||||
|
next = e->entries.le_next;
|
||||||
|
if(e->state >= 100)
|
||||||
|
{
|
||||||
|
LIST_REMOVE(e, entries);
|
||||||
|
Delete_upnphttp(e);
|
||||||
|
}
|
||||||
|
e = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown:
|
||||||
|
/* close out open sockets */
|
||||||
|
while(upnphttphead.lh_first != NULL)
|
||||||
|
{
|
||||||
|
e = upnphttphead.lh_first;
|
||||||
|
LIST_REMOVE(e, entries);
|
||||||
|
Delete_upnphttp(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sudp >= 0) close(sudp);
|
||||||
|
if (shttpl >= 0) close(shttpl);
|
||||||
|
|
||||||
|
if(SendSSDPGoodbye(snotify, n_lan_addr) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "Failed to broadcast good-bye notifications");
|
||||||
|
}
|
||||||
|
for(i=0; i<n_lan_addr; i++)/* for(i=0; i<v.n_lan_addr; i++)*/
|
||||||
|
close(snotify[i]);
|
||||||
|
|
||||||
|
sqlite3_close(db);
|
||||||
|
|
||||||
|
if(unlink(pidfilename) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "Failed to remove pidfile %s: %m", pidfilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
closelog();
|
||||||
|
freeoptions();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
473
minissdp.c
Normal file
473
minissdp.c
Normal file
@ -0,0 +1,473 @@
|
|||||||
|
/* $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 <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include "config.h"
|
||||||
|
#include "upnpdescstrings.h"
|
||||||
|
#include "miniupnpdpath.h"
|
||||||
|
#include "upnphttp.h"
|
||||||
|
#include "upnpglobalvars.h"
|
||||||
|
#include "minissdp.h"
|
||||||
|
|
||||||
|
/* SSDP ip/port */
|
||||||
|
#define SSDP_PORT (1900)
|
||||||
|
#define SSDP_MCAST_ADDR ("239.255.255.250")
|
||||||
|
|
||||||
|
static int
|
||||||
|
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);*/
|
||||||
|
|
||||||
|
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
struct sockaddr_in sockname;
|
||||||
|
|
||||||
|
if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "socket(udp): %m");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
/* 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);*/
|
||||||
|
|
||||||
|
if(bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "bind(udp): %m");
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = n_lan_addr;
|
||||||
|
while(i>0)
|
||||||
|
{
|
||||||
|
i--;
|
||||||
|
if(AddMulticastMembership(s, lan_addr[i].addr.s_addr) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_WARNING,
|
||||||
|
"Failed to add multicast membership for address %s",
|
||||||
|
lan_addr[i].str );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* open the UDP socket used to send SSDP notifications to
|
||||||
|
* the multicast group reserved for them */
|
||||||
|
static int
|
||||||
|
OpenAndConfSSDPNotifySocket(in_addr_t addr)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
unsigned char loopchar = 0;
|
||||||
|
int bcast = 1;
|
||||||
|
struct in_addr mc_if;
|
||||||
|
struct sockaddr_in sockname;
|
||||||
|
|
||||||
|
if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "socket(udp_notify): %m");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mc_if.s_addr = addr; /*inet_addr(addr);*/
|
||||||
|
|
||||||
|
if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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);*/
|
||||||
|
|
||||||
|
if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
|
||||||
|
{
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr[i].addr.s_addr);
|
||||||
|
if(sockets[i] < 0)
|
||||||
|
{
|
||||||
|
for(j=0; j<i; j++)
|
||||||
|
{
|
||||||
|
close(sockets[j]);
|
||||||
|
sockets[j] = -1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* response from a LiveBox (Wanadoo)
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
CACHE-CONTROL: max-age=1800
|
||||||
|
DATE: Thu, 01 Jan 1970 04:03:23 GMT
|
||||||
|
EXT:
|
||||||
|
LOCATION: http://192.168.0.1:49152/gatedesc.xml
|
||||||
|
SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
|
||||||
|
ST: upnp:rootdevice
|
||||||
|
USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
|
||||||
|
|
||||||
|
* response from a Linksys 802.11b :
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Cache-Control:max-age=120
|
||||||
|
Location:http://192.168.5.1:5678/rootDesc.xml
|
||||||
|
Server:NT/5.0 UPnP/1.0
|
||||||
|
ST:upnp:rootdevice
|
||||||
|
USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
|
||||||
|
EXT:
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* not really an SSDP "announce" as it is the response
|
||||||
|
* to a SSDP "M-SEARCH" */
|
||||||
|
static void
|
||||||
|
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;
|
||||||
|
char buf[512];
|
||||||
|
/* TODO :
|
||||||
|
* follow guideline from document "UPnP Device Architecture 1.0"
|
||||||
|
* put in uppercase.
|
||||||
|
* DATE: is recommended
|
||||||
|
* SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
|
||||||
|
* - check what to put in the 'Cache-Control' header
|
||||||
|
* */
|
||||||
|
char szTime[30];
|
||||||
|
time_t tTime = time(NULL);
|
||||||
|
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=1810\r\n"
|
||||||
|
"DATE: %s\r\n"
|
||||||
|
"Ext:\r\n"
|
||||||
|
"ST: %.*s%s\r\n"
|
||||||
|
"USN: %s::%.*s%s\r\n"
|
||||||
|
"EXT:\r\n"
|
||||||
|
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
|
||||||
|
"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
|
||||||
|
"Content-Length: 0\r\n"
|
||||||
|
"\r\n",
|
||||||
|
szTime,
|
||||||
|
st_len, st, suffix,
|
||||||
|
uuidvalue, st_len, st, suffix,
|
||||||
|
host, (unsigned int)port);
|
||||||
|
n = sendto(s, buf, l, 0,
|
||||||
|
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
|
||||||
|
if(n < 0)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
struct sockaddr_in sockname;
|
||||||
|
int l, n, dup, i=0;
|
||||||
|
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);
|
||||||
|
|
||||||
|
for( dup=0; dup<2; dup++ )
|
||||||
|
{
|
||||||
|
if( dup )
|
||||||
|
usleep(200000);
|
||||||
|
i=0;
|
||||||
|
while(known_service_types[i])
|
||||||
|
{
|
||||||
|
l = snprintf(bufr, sizeof(bufr),
|
||||||
|
"NOTIFY * HTTP/1.1\r\n"
|
||||||
|
"HOST:%s:%d\r\n"
|
||||||
|
"CACHE-CONTROL:max-age=%u\r\n"
|
||||||
|
"LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
|
||||||
|
"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
|
||||||
|
"NT:%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==0?"":"1"),
|
||||||
|
uuidvalue, known_service_types[i], (i==0?"":"1") );
|
||||||
|
if(l>=sizeof(bufr))
|
||||||
|
{
|
||||||
|
syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
|
||||||
|
l = sizeof(bufr);
|
||||||
|
}
|
||||||
|
//DEBUG printf("Sending NOTIFY:\n%s", bufr);
|
||||||
|
n = sendto(s, bufr, l, 0,
|
||||||
|
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
|
||||||
|
if(n < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s, host);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SendSSDPNotifies2(int * sockets,
|
||||||
|
unsigned short port,
|
||||||
|
unsigned int lifetime)
|
||||||
|
/*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
|
||||||
|
unsigned short port,
|
||||||
|
unsigned int lifetime)*/
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i=0; i<n_lan_addr; i++)
|
||||||
|
{
|
||||||
|
SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ProcessSSDPRequest()
|
||||||
|
* process SSDP M-SEARCH requests and responds to them */
|
||||||
|
void
|
||||||
|
ProcessSSDPRequest(int s, unsigned short port)
|
||||||
|
/*ProcessSSDPRequest(int s, struct lan_addr_s * lan_addr, int n_lan_addr,
|
||||||
|
unsigned short port)*/
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
char bufr[1500];
|
||||||
|
socklen_t len_r;
|
||||||
|
struct sockaddr_in sendername;
|
||||||
|
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), 0,
|
||||||
|
(struct sockaddr *)&sendername, &len_r);
|
||||||
|
if(n < 0)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "recvfrom(udp): %m");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(memcmp(bufr, "NOTIFY", 6) == 0)
|
||||||
|
{
|
||||||
|
/* ignore NOTIFY packets. We could log the sender and device type */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(memcmp(bufr, "M-SEARCH", 8) == 0)
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
while(i < n)
|
||||||
|
{
|
||||||
|
while(bufr[i] != '\r' || bufr[i+1] != '\n')
|
||||||
|
i++;
|
||||||
|
i += 2;
|
||||||
|
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++;
|
||||||
|
/*syslog(LOG_INFO, "ST: %.*s", st_len, st);*/
|
||||||
|
/*j = 0;*/
|
||||||
|
/*while(bufr[i+j]!='\r') j++;*/
|
||||||
|
/*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*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 )
|
||||||
|
{
|
||||||
|
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(st)
|
||||||
|
{
|
||||||
|
/* 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++)
|
||||||
|
{
|
||||||
|
if( (sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr)
|
||||||
|
== (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
|
||||||
|
{
|
||||||
|
lan_addr_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Responds to request with a device as ST header */
|
||||||
|
for(i = 0; known_service_types[i]; i++)
|
||||||
|
{
|
||||||
|
l = (int)strlen(known_service_types[i]);
|
||||||
|
if(l<=st_len && (0 == memcmp(st, known_service_types[i], l)))
|
||||||
|
{
|
||||||
|
SendSSDPAnnounce2(s, sendername,
|
||||||
|
st, st_len, "",
|
||||||
|
lan_addr[lan_addr_index].str, port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Responds to request with ST: ssdp:all */
|
||||||
|
/* strlen("ssdp:all") == 8 */
|
||||||
|
if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
|
||||||
|
{
|
||||||
|
for(i=0; known_service_types[i]; i++)
|
||||||
|
{
|
||||||
|
l = (int)strlen(known_service_types[i]);
|
||||||
|
SendSSDPAnnounce2(s, sendername,
|
||||||
|
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
|
||||||
|
{
|
||||||
|
syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s:%d",
|
||||||
|
inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
syslog(LOG_NOTICE, "Unknown udp packet received from %s:%d",
|
||||||
|
inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This will broadcast ssdp:byebye notifications to inform
|
||||||
|
* the network that UPnP is going down. */
|
||||||
|
int
|
||||||
|
SendSSDPGoodbye(int * sockets, int n_sockets)
|
||||||
|
{
|
||||||
|
struct sockaddr_in sockname;
|
||||||
|
int n, l;
|
||||||
|
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);
|
||||||
|
|
||||||
|
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\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)
|
||||||
|
{
|
||||||
|
syslog(LOG_ERR, "sendto(udp_shutdown=%d): %m", sockets[j]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
43
minissdp.h
Normal file
43
minissdp.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* 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 __MINISSDP_H__
|
||||||
|
#define __MINISSDP_H__
|
||||||
|
|
||||||
|
/*#include "miniupnpdtypes.h"*/
|
||||||
|
|
||||||
|
int
|
||||||
|
OpenAndConfSSDPReceiveSocket();
|
||||||
|
/* OpenAndConfSSDPReceiveSocket(int n_lan_addr, struct lan_addr_s * lan_addr);*/
|
||||||
|
|
||||||
|
/*int
|
||||||
|
OpenAndConfSSDPNotifySocket(const char * addr);*/
|
||||||
|
|
||||||
|
int
|
||||||
|
OpenAndConfSSDPNotifySockets(int * sockets);
|
||||||
|
/*OpenAndConfSSDPNotifySockets(int * sockets,
|
||||||
|
struct lan_addr_s * lan_addr, int n_lan_addr);*/
|
||||||
|
|
||||||
|
/*void
|
||||||
|
SendSSDPNotifies(int s, const char * host, unsigned short port,
|
||||||
|
unsigned int lifetime);*/
|
||||||
|
void
|
||||||
|
SendSSDPNotifies2(int * sockets,
|
||||||
|
unsigned short port,
|
||||||
|
unsigned int lifetime);
|
||||||
|
/*SendSSDPNotifies2(int * sockets, struct lan_addr_s * lan_addr, int n_lan_addr,
|
||||||
|
unsigned short port,
|
||||||
|
unsigned int lifetime);*/
|
||||||
|
|
||||||
|
void
|
||||||
|
ProcessSSDPRequest(int s, unsigned short port);
|
||||||
|
/*ProcessSSDPRequest(int s, struct lan_addr_s * lan_addr, int n_lan_addr,
|
||||||
|
unsigned short port);*/
|
||||||
|
|
||||||
|
int
|
||||||
|
SendSSDPGoodbye(int * sockets, int n);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
73
miniupnpd.1
Normal file
73
miniupnpd.1
Normal file
@ -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
|
73
miniupnpd.conf
Normal file
73
miniupnpd.conf
Normal file
@ -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
|
47
miniupnpdpath.h
Normal file
47
miniupnpdpath.h
Normal file
@ -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
|
||||||
|
|
23
miniupnpdtypes.h
Normal file
23
miniupnpdtypes.h
Normal file
@ -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
|
191
minixml.c
Normal file
191
minixml.c
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/* $Id$ */
|
||||||
|
/* minixml.c : the minimum size a xml parser can be ! */
|
||||||
|
/* Project : miniupnp
|
||||||
|
* webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||||
|
* Author : Thomas Bernard
|
||||||
|
|
||||||
|
Copyright (c) 2005-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.
|
||||||
|
*/
|
||||||
|
#include "minixml.h"
|
||||||
|
|
||||||
|
/* parseatt : used to parse the argument list
|
||||||
|
* return 0 (false) in case of success and -1 (true) if the end
|
||||||
|
* of the xmlbuffer is reached. */
|
||||||
|
int parseatt(struct xmlparser * p)
|
||||||
|
{
|
||||||
|
const char * attname;
|
||||||
|
int attnamelen;
|
||||||
|
const char * attvalue;
|
||||||
|
int attvaluelen;
|
||||||
|
while(p->xml < p->xmlend)
|
||||||
|
{
|
||||||
|
if(*p->xml=='/' || *p->xml=='>')
|
||||||
|
return 0;
|
||||||
|
if( !IS_WHITE_SPACE(*p->xml) )
|
||||||
|
{
|
||||||
|
char sep;
|
||||||
|
attname = p->xml;
|
||||||
|
attnamelen = 0;
|
||||||
|
while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
|
||||||
|
{
|
||||||
|
attnamelen++; p->xml++;
|
||||||
|
if(p->xml >= p->xmlend)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while(*(p->xml++) != '=')
|
||||||
|
{
|
||||||
|
if(p->xml >= p->xmlend)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while(IS_WHITE_SPACE(*p->xml))
|
||||||
|
{
|
||||||
|
p->xml++;
|
||||||
|
if(p->xml >= p->xmlend)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sep = *p->xml;
|
||||||
|
if(sep=='\'' || sep=='\"')
|
||||||
|
{
|
||||||
|
p->xml++;
|
||||||
|
if(p->xml >= p->xmlend)
|
||||||
|
return -1;
|
||||||
|
attvalue = p->xml;
|
||||||
|
attvaluelen = 0;
|
||||||
|
while(*p->xml != sep)
|
||||||
|
{
|
||||||
|
attvaluelen++; p->xml++;
|
||||||
|
if(p->xml >= p->xmlend)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
attvalue = p->xml;
|
||||||
|
attvaluelen = 0;
|
||||||
|
while( !IS_WHITE_SPACE(*p->xml)
|
||||||
|
&& *p->xml != '>' && *p->xml != '/')
|
||||||
|
{
|
||||||
|
attvaluelen++; p->xml++;
|
||||||
|
if(p->xml >= p->xmlend)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*printf("%.*s='%.*s'\n",
|
||||||
|
attnamelen, attname, attvaluelen, attvalue);*/
|
||||||
|
if(p->attfunc)
|
||||||
|
p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
|
||||||
|
}
|
||||||
|
p->xml++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parseelt parse the xml stream and
|
||||||
|
* call the callback functions when needed... */
|
||||||
|
void parseelt(struct xmlparser * p)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const char * elementname;
|
||||||
|
while(p->xml < (p->xmlend - 1))
|
||||||
|
{
|
||||||
|
if((p->xml)[0]=='<' && (p->xml)[1]!='?')
|
||||||
|
{
|
||||||
|
i = 0; elementname = ++p->xml;
|
||||||
|
while( !IS_WHITE_SPACE(*p->xml)
|
||||||
|
&& (*p->xml!='>') && (*p->xml!='/')
|
||||||
|
)
|
||||||
|
{
|
||||||
|
i++; p->xml++;
|
||||||
|
if (p->xml >= p->xmlend)
|
||||||
|
return;
|
||||||
|
/* to ignore namespace : */
|
||||||
|
if(*p->xml==':')
|
||||||
|
{
|
||||||
|
i = 0;
|
||||||
|
elementname = ++p->xml;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(i>0)
|
||||||
|
{
|
||||||
|
if(p->starteltfunc)
|
||||||
|
p->starteltfunc(p->data, elementname, i);
|
||||||
|
if(parseatt(p))
|
||||||
|
return;
|
||||||
|
if(*p->xml!='/')
|
||||||
|
{
|
||||||
|
const char * data;
|
||||||
|
i = 0; data = ++p->xml;
|
||||||
|
if (p->xml >= p->xmlend)
|
||||||
|
return;
|
||||||
|
while( IS_WHITE_SPACE(*p->xml) )
|
||||||
|
{
|
||||||
|
p->xml++;
|
||||||
|
if (p->xml >= p->xmlend)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while(*p->xml!='<')
|
||||||
|
{
|
||||||
|
i++; p->xml++;
|
||||||
|
if (p->xml >= p->xmlend)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(i>0 && p->datafunc)
|
||||||
|
p->datafunc(p->data, data, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(*p->xml == '/')
|
||||||
|
{
|
||||||
|
i = 0; elementname = ++p->xml;
|
||||||
|
if (p->xml >= p->xmlend)
|
||||||
|
return;
|
||||||
|
while((*p->xml != '>'))
|
||||||
|
{
|
||||||
|
i++; p->xml++;
|
||||||
|
if (p->xml >= p->xmlend)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(p->endeltfunc)
|
||||||
|
p->endeltfunc(p->data, elementname, i);
|
||||||
|
p->xml++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p->xml++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the parser must be initialized before calling this function */
|
||||||
|
void parsexml(struct xmlparser * parser)
|
||||||
|
{
|
||||||
|
parser->xml = parser->xmlstart;
|
||||||
|
parser->xmlend = parser->xmlstart + parser->xmlsize;
|
||||||
|
parseelt(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
36
minixml.h
Normal file
36
minixml.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* minimal xml parser
|
||||||
|
*
|
||||||
|
* Project : miniupnp
|
||||||
|
* Website : http://miniupnp.free.fr/
|
||||||
|
* Author : Thomas Bernard
|
||||||
|
* 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'))
|
||||||
|
|
||||||
|
/* if a callback function pointer is set to NULL,
|
||||||
|
* the function is not called */
|
||||||
|
struct xmlparser {
|
||||||
|
const char *xmlstart;
|
||||||
|
const char *xmlend;
|
||||||
|
const char *xml; /* pointer to current character */
|
||||||
|
int xmlsize;
|
||||||
|
void * data;
|
||||||
|
void (*starteltfunc) (void *, const char *, int);
|
||||||
|
void (*endeltfunc) (void *, const char *, int);
|
||||||
|
void (*datafunc) (void *, const char *, int);
|
||||||
|
void (*attfunc) (void *, const char *, int, const char *, int);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* parsexml()
|
||||||
|
* the xmlparser structure must be initialized before the call
|
||||||
|
* the following structure members have to be initialized :
|
||||||
|
* xmlstart, xmlsize, data, *func
|
||||||
|
* xml is for internal usage, xmlend is computed automatically */
|
||||||
|
void parsexml(struct xmlparser *);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
173
options.c
Normal file
173
options.c
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
/* $Id$ */
|
||||||
|
/* MiniUPnP project
|
||||||
|
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||||
|
* author: Ryan Wagoner
|
||||||
|
* (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"
|
||||||
|
|
||||||
|
struct option * ary_options = NULL;
|
||||||
|
int num_options = 0;
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
enum upnpconfigoptions id;
|
||||||
|
const char * name;
|
||||||
|
} optionids[] = {
|
||||||
|
{ 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_NUMBER, "model_number"},
|
||||||
|
{ 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"},
|
||||||
|
{ UPNPSECUREMODE, "secure_mode"}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
readoptionsfile(const char * fname)
|
||||||
|
{
|
||||||
|
FILE *hfile = NULL;
|
||||||
|
char buffer[1024];
|
||||||
|
char *equals;
|
||||||
|
char *name;
|
||||||
|
char *value;
|
||||||
|
char *t;
|
||||||
|
int linenum = 0;
|
||||||
|
int i;
|
||||||
|
enum upnpconfigoptions id;
|
||||||
|
|
||||||
|
if(!fname || (strlen(fname) == 0))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("Reading configuration from file %s\n", fname);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(!(hfile = fopen(fname, "r")))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(ary_options != NULL)
|
||||||
|
{
|
||||||
|
free(ary_options);
|
||||||
|
num_options = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(fgets(buffer, sizeof(buffer), hfile))
|
||||||
|
{
|
||||||
|
linenum++;
|
||||||
|
t = strchr(buffer, '\n');
|
||||||
|
if(t)
|
||||||
|
{
|
||||||
|
*t = '\0';
|
||||||
|
t--;
|
||||||
|
while((t >= buffer) && isspace(*t))
|
||||||
|
{
|
||||||
|
*t = '\0';
|
||||||
|
t--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip leading whitespaces */
|
||||||
|
name = buffer;
|
||||||
|
while(isspace(*name))
|
||||||
|
name++;
|
||||||
|
|
||||||
|
/* check for comments or empty lines */
|
||||||
|
if(name[0] == '#' || name[0] == '\0') continue;
|
||||||
|
|
||||||
|
if(!(equals = strchr(name, '=')))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "parsing error file %s line %d : %s\n",
|
||||||
|
fname, linenum, name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove ending whitespaces */
|
||||||
|
for(t=equals-1; t>name && isspace(*t); t--)
|
||||||
|
*t = '\0';
|
||||||
|
|
||||||
|
*equals = '\0';
|
||||||
|
value = equals+1;
|
||||||
|
|
||||||
|
/* skip leading whitespaces */
|
||||||
|
while(isspace(*value))
|
||||||
|
value++;
|
||||||
|
|
||||||
|
id = UPNP_INVALID;
|
||||||
|
for(i=0; i<sizeof(optionids)/sizeof(optionids[0]); i++)
|
||||||
|
{
|
||||||
|
/*printf("%2d %2d %s %s\n", i, optionids[i].id, name,
|
||||||
|
optionids[i].name); */
|
||||||
|
|
||||||
|
if(0 == strcmp(name, optionids[i].name))
|
||||||
|
{
|
||||||
|
id = optionids[i].id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(id == UPNP_INVALID)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "parsing error file %s line %d : %s=%s\n",
|
||||||
|
fname, linenum, name, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
num_options += 1;
|
||||||
|
ary_options = (struct option *) realloc(ary_options, num_options * sizeof(struct option));
|
||||||
|
|
||||||
|
ary_options[num_options-1].id = id;
|
||||||
|
strncpy(ary_options[num_options-1].value, value, MAX_OPTION_VALUE_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(hfile);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
freeoptions(void)
|
||||||
|
{
|
||||||
|
if(ary_options)
|
||||||
|
{
|
||||||
|
free(ary_options);
|
||||||
|
ary_options = NULL;
|
||||||
|
num_options = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
69
options.h
Normal file
69
options.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/* MiniUPnP project
|
||||||
|
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||||
|
* author: Ryan Wagoner
|
||||||
|
* (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__
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
/* enum of option available in the miniupnpd.conf */
|
||||||
|
enum upnpconfigoptions {
|
||||||
|
UPNP_INVALID = 0,
|
||||||
|
UPNPEXT_IFNAME = 1, /* 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_NUMBER, /* model_number */
|
||||||
|
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 */
|
||||||
|
UPNPENABLE /* enable_upnp */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* readoptionsfile()
|
||||||
|
* parse and store the option file values
|
||||||
|
* returns: 0 success, -1 failure */
|
||||||
|
int
|
||||||
|
readoptionsfile(const char * fname);
|
||||||
|
|
||||||
|
/* freeoptions()
|
||||||
|
* frees memory allocated to option values */
|
||||||
|
void
|
||||||
|
freeoptions(void);
|
||||||
|
|
||||||
|
#define MAX_OPTION_VALUE_LEN (80)
|
||||||
|
struct option
|
||||||
|
{
|
||||||
|
enum upnpconfigoptions id;
|
||||||
|
char value[MAX_OPTION_VALUE_LEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct option * ary_options;
|
||||||
|
extern int num_options;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
510
scanner.c
Normal file
510
scanner.c
Normal file
@ -0,0 +1,510 @@
|
|||||||
|
/* MiniDLNA media server
|
||||||
|
* Copyright (C) 2008 Justin Maggard
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
#include "upnpglobalvars.h"
|
||||||
|
#include "metadata.h"
|
||||||
|
#include "sql.h"
|
||||||
|
#include "scanner.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
ends_with(const char * haystack, const char * needle)
|
||||||
|
{
|
||||||
|
const char *found = strcasestr(haystack, needle);
|
||||||
|
return (found && found[strlen(needle)] == '\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
is_video(const char * file)
|
||||||
|
{
|
||||||
|
return (ends_with(file, ".mpg") || ends_with(file, ".mpeg") ||
|
||||||
|
ends_with(file, ".ts") || ends_with(file, ".avi"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
is_audio(const char * file)
|
||||||
|
{
|
||||||
|
return (ends_with(file, ".mp3") ||
|
||||||
|
ends_with(file, ".m4a") || ends_with(file, ".aac"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
is_image(const char * file)
|
||||||
|
{
|
||||||
|
return (ends_with(file, ".jpg") || ends_with(file, ".jpeg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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) )
|
||||||
|
{
|
||||||
|
parentID++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parentID = 0;
|
||||||
|
}
|
||||||
|
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_table(result);
|
||||||
|
|
||||||
|
return (long long)parentID<<32|objectID;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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 = 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);
|
||||||
|
if( date )
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if( cam && date )
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
/* All Images */
|
||||||
|
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") )
|
||||||
|
{
|
||||||
|
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( artist )
|
||||||
|
{
|
||||||
|
if( strcmp(artist, last_artist) == 0 )
|
||||||
|
{
|
||||||
|
objectID = ++last_artist_objectID;
|
||||||
|
parentID = last_artist_parentID;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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 = 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( album )
|
||||||
|
{
|
||||||
|
if( strcmp(album, last_album) == 0 )
|
||||||
|
{
|
||||||
|
objectID = ++last_album_objectID;
|
||||||
|
parentID = last_album_parentID;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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( strcmp(genre, last_genre) == 0 )
|
||||||
|
{
|
||||||
|
objectID = ++last_genre_objectID;
|
||||||
|
parentID = last_genre_parentID;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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 */
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
insert_directory(const char * name, const char * path, const char * parentID, int objectID)
|
||||||
|
{
|
||||||
|
char *sql;
|
||||||
|
int ret, i;
|
||||||
|
char class[] = "container.storageFolder";
|
||||||
|
const char * const base[] = { BROWSEDIR_ID, MUSIC_DIR_ID, VIDEO_DIR_ID, IMAGE_DIR_ID, 0 };
|
||||||
|
|
||||||
|
for( i=0; base[i]; i++ )
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
insert_file(char * name, const char * path, const char * parentID, int object)
|
||||||
|
{
|
||||||
|
char *sql;
|
||||||
|
char class[32];
|
||||||
|
char objectID[64];
|
||||||
|
unsigned long int detailID = 0;
|
||||||
|
char base[8];
|
||||||
|
|
||||||
|
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) )
|
||||||
|
{
|
||||||
|
strcpy(base, IMAGE_DIR_ID);
|
||||||
|
strcpy(class, "item.imageItem");
|
||||||
|
detailID = GetImageMetadata(path, name);
|
||||||
|
}
|
||||||
|
else if( is_audio(name) )
|
||||||
|
{
|
||||||
|
strcpy(base, MUSIC_DIR_ID);
|
||||||
|
strcpy(class, "item.audioItem.musicTrack");
|
||||||
|
detailID = GetAudioMetadata(path, name);
|
||||||
|
}
|
||||||
|
else if( is_video(name) )
|
||||||
|
{
|
||||||
|
strcpy(base, VIDEO_DIR_ID);
|
||||||
|
strcpy(class, "item.videoItem");
|
||||||
|
detailID = GetVideoMetadata(path, 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);
|
||||||
|
#endif
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
create_database(void)
|
||||||
|
{
|
||||||
|
int ret, i;
|
||||||
|
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 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, "
|
||||||
|
"SIZE INTEGER, "
|
||||||
|
"TITLE TEXT, "
|
||||||
|
"DURATION TEXT, "
|
||||||
|
"BITRATE INTEGER, "
|
||||||
|
"SAMPLERATE INTEGER, "
|
||||||
|
"ARTIST TEXT, "
|
||||||
|
"ALBUM TEXT, "
|
||||||
|
"GENRE TEXT, "
|
||||||
|
"COMMENT TEXT, "
|
||||||
|
"CHANNELS INTEGER, "
|
||||||
|
"TRACK INTEGER, "
|
||||||
|
"DATE DATE, "
|
||||||
|
"WIDTH TEXT, "
|
||||||
|
"HEIGHT TEXT, "
|
||||||
|
"THUMBNAIL BOOL DEFAULT 0, "
|
||||||
|
"CREATOR TEXT, "
|
||||||
|
"DLNA_PN TEXT, "
|
||||||
|
"MIME TEXT"
|
||||||
|
");");
|
||||||
|
if( ret != SQLITE_OK )
|
||||||
|
goto sql_failed;
|
||||||
|
for( i=0; containers[i]; i=i+3 )
|
||||||
|
{
|
||||||
|
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_OBJECTS_PATH ON OBJECTS(PATH);");
|
||||||
|
sql_exec(db, "create INDEX IDX_DETAILS_ID ON DETAILS(ID);");
|
||||||
|
|
||||||
|
|
||||||
|
sql_failed:
|
||||||
|
if( ret != SQLITE_OK )
|
||||||
|
fprintf(stderr, "Error creating SQLite3 database!\n");
|
||||||
|
return (ret != SQLITE_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
filter_media(const struct dirent *d)
|
||||||
|
{
|
||||||
|
struct stat entry;
|
||||||
|
return ( (*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)
|
||||||
|
{
|
||||||
|
struct dirent **namelist;
|
||||||
|
struct stat entry;
|
||||||
|
int n, i;
|
||||||
|
char parent_id[PATH_MAX];
|
||||||
|
char full_path[PATH_MAX];
|
||||||
|
char * name;
|
||||||
|
|
||||||
|
if( !parent )
|
||||||
|
{
|
||||||
|
if( create_database() != 0 )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error creating database!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setlocale(LC_COLLATE, "");
|
||||||
|
if( chdir(dir) != 0 )
|
||||||
|
return;
|
||||||
|
n = scandir(".", &namelist, filter_media, alphasort);
|
||||||
|
if (n < 0) {
|
||||||
|
fprintf(stderr, "Error scanning %s [scandir]\n", dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i=0; i < n; i++) {
|
||||||
|
if( stat(namelist[i]->d_name, &entry) == 0 )
|
||||||
|
{
|
||||||
|
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;", 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( name )
|
||||||
|
free(name);
|
||||||
|
free(namelist[i]);
|
||||||
|
}
|
||||||
|
free(namelist);
|
||||||
|
chdir("..");
|
||||||
|
}
|
21
scanner.h
Normal file
21
scanner.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* Media file scanner
|
||||||
|
*
|
||||||
|
* Project : minidlna
|
||||||
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
|
* Author : Justin Maggard
|
||||||
|
* 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__
|
||||||
|
|
||||||
|
#define BROWSEDIR_ID "64"
|
||||||
|
#define MUSIC_DIR_ID "1$20"
|
||||||
|
#define VIDEO_DIR_ID "2$21"
|
||||||
|
#define IMAGE_DIR_ID "3$22"
|
||||||
|
|
||||||
|
void
|
||||||
|
ScanDirectory(const char * dir, const char * parent);
|
||||||
|
|
||||||
|
#endif
|
53
sql.c
Normal file
53
sql.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/* MiniDLNA media server
|
||||||
|
* Copyright (C) 2008 Justin Maggard
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "sql.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
sql_exec(sqlite3 * db, const char * sql)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char *errMsg = NULL;
|
||||||
|
//DEBUG printf("SQL: %s\n", sql);
|
||||||
|
|
||||||
|
//ret = sqlite3_exec(db, sql, 0, 0, &errMsg);
|
||||||
|
ret = sqlite3_exec(db, sql, 0, 0, &errMsg);
|
||||||
|
if( ret != SQLITE_OK )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "SQL ERROR %d [%s]\n%s\n", ret, errMsg, sql);
|
||||||
|
if (errMsg)
|
||||||
|
sqlite3_free(errMsg);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sql_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int *pnColumn, char **pzErrmsg)
|
||||||
|
{
|
||||||
|
//DEBUG printf("SQL: %s\n", zSql);
|
||||||
|
int ret;
|
||||||
|
ret = sqlite3_get_table(db, zSql, pazResult, pnRow, pnColumn, pzErrmsg);
|
||||||
|
if( ret != SQLITE_OK )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "SQL ERROR [%s]\n%s\n", *pzErrmsg, zSql);
|
||||||
|
if (*pzErrmsg)
|
||||||
|
sqlite3_free(*pzErrmsg);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
21
sql.h
Normal file
21
sql.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* Reusable SQLite3 wrapper functions
|
||||||
|
*
|
||||||
|
* Project : minidlna
|
||||||
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
|
* Author : Justin Maggard
|
||||||
|
* 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 * sql);
|
||||||
|
|
||||||
|
int
|
||||||
|
sql_get_table(sqlite3 *db, const char *zSql, char ***pazResult, int *pnRow, int *pnColumn, char **pzErrmsg);
|
||||||
|
|
||||||
|
#endif
|
121
testupnpdescgen.c
Normal file
121
testupnpdescgen.c
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/* $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 <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "upnpdescgen.h"
|
||||||
|
|
||||||
|
char uuidvalue[] = "uuid:12345678-0000-0000-0000-00000000abcd";
|
||||||
|
char serialnumber[] = "12345678";
|
||||||
|
char modelnumber[] = "1";
|
||||||
|
char presentationurl[] = "http://192.168.0.1:8080/";
|
||||||
|
|
||||||
|
char * use_ext_ip_addr = NULL;
|
||||||
|
const char * ext_if_name = "eth0";
|
||||||
|
|
||||||
|
int getifaddr(const char * ifname, char * buf, int len)
|
||||||
|
{
|
||||||
|
strncpy(buf, "1.2.3.4", len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int upnp_get_portmapping_number_of_entries()
|
||||||
|
{
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To be improved */
|
||||||
|
int
|
||||||
|
xml_pretty_print(const char * s, int len, FILE * f)
|
||||||
|
{
|
||||||
|
int n = 0, i;
|
||||||
|
int elt_close = 0;
|
||||||
|
int c, indent = 0;
|
||||||
|
while(len > 0)
|
||||||
|
{
|
||||||
|
c = *(s++); len--;
|
||||||
|
switch(c)
|
||||||
|
{
|
||||||
|
case '<':
|
||||||
|
if(len>0 && *s == '/')
|
||||||
|
elt_close++;
|
||||||
|
else if(len>0 && *s == '?')
|
||||||
|
elt_close = 1;
|
||||||
|
else
|
||||||
|
elt_close = 0;
|
||||||
|
if(elt_close!=1)
|
||||||
|
{
|
||||||
|
if(elt_close > 1)
|
||||||
|
indent--;
|
||||||
|
fputc('\n', f); n++;
|
||||||
|
for(i=indent; i>0; i--)
|
||||||
|
fputc(' ', f);
|
||||||
|
n += indent;
|
||||||
|
}
|
||||||
|
fputc(c, f); n++;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
fputc(c, f); n++;
|
||||||
|
if(elt_close==1)
|
||||||
|
{
|
||||||
|
/*fputc('\n', f); n++; */
|
||||||
|
//elt_close = 0;
|
||||||
|
if(indent > 0)
|
||||||
|
indent--;
|
||||||
|
}
|
||||||
|
else if(elt_close == 0)
|
||||||
|
indent++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fputc(c, f); n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stupid test */
|
||||||
|
const char * str1 = "Prefix123String";
|
||||||
|
const char * str2 = "123String";
|
||||||
|
|
||||||
|
void stupid_test()
|
||||||
|
{
|
||||||
|
printf("str1:'%s' str2:'%s'\n", str1, str2);
|
||||||
|
printf("str1:%p str2:%p str2-str1:%ld\n", str1, str2, (long)(str2-str1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* main */
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char * * argv)
|
||||||
|
{
|
||||||
|
char * rootDesc;
|
||||||
|
int rootDescLen;
|
||||||
|
char * s;
|
||||||
|
int l;
|
||||||
|
rootDesc = genRootDesc(&rootDescLen);
|
||||||
|
xml_pretty_print(rootDesc, rootDescLen, stdout);
|
||||||
|
free(rootDesc);
|
||||||
|
printf("\n-------------\n");
|
||||||
|
s = genContentDirectory(&l);
|
||||||
|
xml_pretty_print(s, l, stdout);
|
||||||
|
free(s);
|
||||||
|
printf("\n-------------\n");
|
||||||
|
s = genConnectionManager(&l);
|
||||||
|
xml_pretty_print(s, l, stdout);
|
||||||
|
free(s);
|
||||||
|
printf("\n-------------\n");
|
||||||
|
#ifdef ENABLE_EVENTS
|
||||||
|
#endif
|
||||||
|
/*
|
||||||
|
stupid_test();
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
933
upnpdescgen.c
Normal file
933
upnpdescgen.c
Normal file
@ -0,0 +1,933 @@
|
|||||||
|
/* $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"
|
||||||
|
#endif
|
||||||
|
#include "upnpdescgen.h"
|
||||||
|
#include "miniupnpdpath.h"
|
||||||
|
#include "upnpglobalvars.h"
|
||||||
|
#include "upnpdescstrings.h"
|
||||||
|
|
||||||
|
static const char * const upnptypes[] =
|
||||||
|
{
|
||||||
|
"string",
|
||||||
|
"boolean",
|
||||||
|
"ui2",
|
||||||
|
"ui4",
|
||||||
|
"i4",
|
||||||
|
"uri",
|
||||||
|
"int",
|
||||||
|
"bin.base64"
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
"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,
|
||||||
|
"", /* 46 */
|
||||||
|
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,2)},
|
||||||
|
{"specVersion", INITHELPER(3,2)},
|
||||||
|
{"device", INITHELPER(5,13)},
|
||||||
|
{"/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 */
|
||||||
|
{"/dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"", "DMS-1.50"},
|
||||||
|
{"/presentationURL", presentationurl}, /* recommended */
|
||||||
|
{"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},
|
||||||
|
{"/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},
|
||||||
|
{"/serviceType", "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"},
|
||||||
|
{"/serviceId", "urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar"},
|
||||||
|
{"/controlURL", X_MS_MEDIARECEIVERREGISTRAR_CONTROLURL},
|
||||||
|
{"/eventSubURL", X_MS_MEDIARECEIVERREGISTRAR_EVENTURL},
|
||||||
|
{"/SCPDURL", X_MS_MEDIARECEIVERREGISTRAR_PATH},
|
||||||
|
{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 */
|
||||||
|
//OPTIONAL {"PrepareForConnection", PrepareForConnectionArgs}, /* R */
|
||||||
|
//OPTIONAL {"ConnectionComplete", ConnectionCompleteArgs}, /* R */
|
||||||
|
{"GetCurrentConnectionIDs", GetCurrentConnectionIDsArgs}, /* R */
|
||||||
|
{"GetCurrentConnectionInfo", GetCurrentConnectionInfoArgs}, /* R */
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct stateVar ConnectionManagerVars[] =
|
||||||
|
{
|
||||||
|
{"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 */
|
||||||
|
{"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 argument SearchArgs[] =
|
||||||
|
{
|
||||||
|
{"ContainerID", 1, 1},
|
||||||
|
{"SearchCriteria", 1, 3},
|
||||||
|
{"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 */
|
||||||
|
{"Search", SearchArgs}, /* O */
|
||||||
|
#if 0 // Not implementing optional features yet...
|
||||||
|
{"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, 46, 46}, /* 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_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 */
|
||||||
|
//JM{"A_ARG_TYPE_TransferLength", 0, 0},
|
||||||
|
//JM{"A_ARG_TYPE_TransferTotal", 0, 0},
|
||||||
|
//JM{"A_ARG_TYPE_TagValueList", 0, 0},
|
||||||
|
//JM{"A_ARG_TYPE_URI", 5, 0}, /* 15 */
|
||||||
|
{"SearchCapabilities", 0, 0},
|
||||||
|
{"SortCapabilities", 0, 0},
|
||||||
|
{"SystemUpdateID", 3|0x80, 0, 46, 46},
|
||||||
|
//{"ContainerUpdateIDs", 0, 0},
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct argument GetIsAuthorizedArgs[] =
|
||||||
|
{
|
||||||
|
{"DeviceID", 1, 0},
|
||||||
|
{"Result", 2, 3},
|
||||||
|
{NULL, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct argument GetIsValidatedArgs[] =
|
||||||
|
{
|
||||||
|
{"DeviceID", 1, 0},
|
||||||
|
{"Result", 2, 3},
|
||||||
|
{NULL, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct argument GetRegisterDeviceArgs[] =
|
||||||
|
{
|
||||||
|
{"RegistrationReqMsg", 1, 1},
|
||||||
|
{"RegistrationRespMsg", 2, 2},
|
||||||
|
{NULL, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct action X_MS_MediaReceiverRegistrarActions[] =
|
||||||
|
{
|
||||||
|
{"IsAuthorized", GetIsAuthorizedArgs}, /* R */
|
||||||
|
{"IsValidated", GetIsValidatedArgs}, /* R */
|
||||||
|
{"RegisterDevice", GetRegisterDeviceArgs}, /* R */
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct stateVar X_MS_MediaReceiverRegistrarVars[] =
|
||||||
|
{
|
||||||
|
{"A_ARG_TYPE_DeviceID", 0, 0},
|
||||||
|
{"A_ARG_TYPE_RegistrationReqMsg", 7, 0},
|
||||||
|
{"A_ARG_TYPE_RegistrationRespMsg", 7, 0},
|
||||||
|
{"A_ARG_TYPE_Result", 6, 0},
|
||||||
|
{"AuthorizationDeniedUpdateID", (1<<7)|3, 0},
|
||||||
|
{"AuthorizationGrantedUpdateID", (1<<7)|3, 0},
|
||||||
|
{"ValidationRevokedUpdateID", (1<<7)|3, 0},
|
||||||
|
{"ValidationSucceededUpdateID", (1<<7)|3, 0},
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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 };
|
||||||
|
|
||||||
|
static const struct serviceDesc scpdX_MS_MediaReceiverRegistrar =
|
||||||
|
{ X_MS_MediaReceiverRegistrarActions, X_MS_MediaReceiverRegistrarVars };
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
char element[64];
|
||||||
|
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, '<');
|
||||||
|
sscanf(eltname, "%s", element);
|
||||||
|
str = strcat_str(str, len, tmplen, element);
|
||||||
|
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;
|
||||||
|
#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() :
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* genX_MS_MediaReceiverRegistrar() :
|
||||||
|
* Generate the X_MS_MediaReceiverRegistrar xml description */
|
||||||
|
char *
|
||||||
|
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;
|
||||||
|
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><");
|
||||||
|
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 */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = strcat_str(str, len, &tmplen, upnpallowedvalues[v->ieventvalue]);
|
||||||
|
//printf("%s", upnpallowedvalues[v->ieventvalue]);
|
||||||
|
}
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
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 *
|
||||||
|
getVarsX_MS_MediaReceiverRegistrar(int * l)
|
||||||
|
{
|
||||||
|
return genEventVars(l,
|
||||||
|
&scpdX_MS_MediaReceiverRegistrar,
|
||||||
|
"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
944
upnpdescgen.c.dlna
Normal file
944
upnpdescgen.c.dlna
Normal file
@ -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
|
94
upnpdescgen.h
Normal file
94
upnpdescgen.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/* 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 __UPNPDESCGEN_H__
|
||||||
|
#define __UPNPDESCGEN_H__
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
/* for the root description
|
||||||
|
* The child list reference is stored in "data" member using the
|
||||||
|
* INITHELPER macro with index/nchild always in the
|
||||||
|
* same order, whatever the endianness */
|
||||||
|
struct XMLElt {
|
||||||
|
const char * eltname; /* begin with '/' if no child */
|
||||||
|
const char * data; /* Value */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* for service description */
|
||||||
|
struct serviceDesc {
|
||||||
|
const struct action * actionList;
|
||||||
|
const struct stateVar * serviceStateTable;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct action {
|
||||||
|
const char * name;
|
||||||
|
const struct argument * args;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct argument {
|
||||||
|
const char * name; /* the name of the argument */
|
||||||
|
unsigned char dir; /* 1 = in, 2 = out */
|
||||||
|
unsigned char relatedVar; /* index of the related variable */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stateVar {
|
||||||
|
const char * name;
|
||||||
|
unsigned char itype; /* MSB: sendEvent flag, 7 LSB: index in upnptypes */
|
||||||
|
unsigned char idefault; /* default value */
|
||||||
|
unsigned char iallowedlist; /* index in allowed values list */
|
||||||
|
unsigned char ieventvalue; /* fixed value returned or magical values */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* little endian
|
||||||
|
* The code has now be tested on big endian architecture */
|
||||||
|
#define INITHELPER(i, n) ((char *)((n<<16)|i))
|
||||||
|
|
||||||
|
/* char * genRootDesc(int *);
|
||||||
|
* returns: NULL on error, string allocated on the heap */
|
||||||
|
char *
|
||||||
|
genRootDesc(int * len);
|
||||||
|
|
||||||
|
/* for the two following functions */
|
||||||
|
char *
|
||||||
|
genContentDirectory(int * len);
|
||||||
|
|
||||||
|
char *
|
||||||
|
genConnectionManager(int * len);
|
||||||
|
|
||||||
|
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 *
|
||||||
|
getVarsWANIPCn(int * len);
|
||||||
|
|
||||||
|
char *
|
||||||
|
getVarsWANCfg(int * len);
|
||||||
|
|
||||||
|
char *
|
||||||
|
getVarsL3F(int * len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
38
upnpdescstrings.h
Normal file
38
upnpdescstrings.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* miniupnp project
|
||||||
|
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||||
|
* (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 */
|
||||||
|
#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
|
||||||
|
|
478
upnpevents.c
Normal file
478
upnpevents.c
Normal file
@ -0,0 +1,478 @@
|
|||||||
|
/* $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 <syslog.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "config.h"
|
||||||
|
#include "upnpevents.h"
|
||||||
|
#include "miniupnpdpath.h"
|
||||||
|
#include "upnpglobalvars.h"
|
||||||
|
#include "upnpdescgen.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_EVENTS
|
||||||
|
/*enum subscriber_service_enum {
|
||||||
|
EWanCFG = 1,
|
||||||
|
EWanIPC,
|
||||||
|
EL3F
|
||||||
|
};*/
|
||||||
|
|
||||||
|
/* stuctures definitions */
|
||||||
|
struct subscriber {
|
||||||
|
LIST_ENTRY(subscriber) entries;
|
||||||
|
struct upnp_event_notify * notify;
|
||||||
|
time_t timeout;
|
||||||
|
uint32_t seq;
|
||||||
|
/*enum { EWanCFG = 1, EWanIPC, EL3F } service;*/
|
||||||
|
enum subscriber_service_enum service;
|
||||||
|
char uuid[42];
|
||||||
|
char callback[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct upnp_event_notify {
|
||||||
|
LIST_ENTRY(upnp_event_notify) entries;
|
||||||
|
int s; /* socket */
|
||||||
|
enum { ECreated=1,
|
||||||
|
EConnecting,
|
||||||
|
ESending,
|
||||||
|
EWaitingForResponse,
|
||||||
|
EFinished,
|
||||||
|
EError } state;
|
||||||
|
struct subscriber * sub;
|
||||||
|
char * buffer;
|
||||||
|
int buffersize;
|
||||||
|
int tosend;
|
||||||
|
int sent;
|
||||||
|
const char * path;
|
||||||
|
char addrstr[16];
|
||||||
|
char portstr[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* prototypes */
|
||||||
|
static void
|
||||||
|
upnp_event_create_notify(struct subscriber * sub);
|
||||||
|
|
||||||
|
/* Subscriber list */
|
||||||
|
LIST_HEAD(listhead, subscriber) subscriberlist = { NULL };
|
||||||
|
|
||||||
|
/* notify list */
|
||||||
|
LIST_HEAD(listheadnotif, upnp_event_notify) notifylist = { NULL };
|
||||||
|
|
||||||
|
/* create a new subscriber */
|
||||||
|
static struct subscriber *
|
||||||
|
newSubscriber(const char * eventurl, const char * callback, int callbacklen)
|
||||||
|
{
|
||||||
|
struct subscriber * tmp;
|
||||||
|
if(!eventurl || !callback || !callbacklen)
|
||||||
|
return NULL;
|
||||||
|
tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1);
|
||||||
|
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, 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;
|
||||||
|
}
|
||||||
|
memcpy(tmp->callback, callback, callbacklen);
|
||||||
|
tmp->callback[callbacklen] = '\0';
|
||||||
|
/* make a dummy uuid */
|
||||||
|
/* TODO: improve that */
|
||||||
|
strncpy(tmp->uuid, uuidvalue, sizeof(tmp->uuid));
|
||||||
|
tmp->uuid[sizeof(tmp->uuid)-1] = '\0';
|
||||||
|
snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates a new subscriber and adds it to the subscriber list
|
||||||
|
* also initiate 1st notify */
|
||||||
|
const char *
|
||||||
|
upnpevents_addSubscriber(const char * eventurl,
|
||||||
|
const char * callback, int callbacklen,
|
||||||
|
int timeout)
|
||||||
|
{
|
||||||
|
struct subscriber * tmp;
|
||||||
|
/*static char uuid[42];*/
|
||||||
|
/* "uuid:00000000-0000-0000-0000-000000000000"; 5+36+1=42bytes */
|
||||||
|
syslog(LOG_DEBUG, "addSubscriber(%s, %.*s, %d)",
|
||||||
|
eventurl, callbacklen, callback, timeout);
|
||||||
|
/*strncpy(uuid, uuidvalue, sizeof(uuid));
|
||||||
|
uuid[sizeof(uuid)-1] = '\0';*/
|
||||||
|
tmp = newSubscriber(eventurl, callback, callbacklen);
|
||||||
|
if(!tmp)
|
||||||
|
return NULL;
|
||||||
|
if(timeout)
|
||||||
|
tmp->timeout = time(NULL) + timeout;
|
||||||
|
LIST_INSERT_HEAD(&subscriberlist, tmp, entries);
|
||||||
|
upnp_event_create_notify(tmp);
|
||||||
|
return tmp->uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* renew a subscription (update the timeout) */
|
||||||
|
int
|
||||||
|
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)) {
|
||||||
|
sub->timeout = (timeout ? time(NULL) + timeout : 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
upnpevents_removeSubscriber(const char * sid, int sidlen)
|
||||||
|
{
|
||||||
|
struct subscriber * sub;
|
||||||
|
if(!sid)
|
||||||
|
return -1;
|
||||||
|
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
|
||||||
|
if(memcmp(sid, sub->uuid, 41)) {
|
||||||
|
if(sub->notify) {
|
||||||
|
sub->notify->sub = NULL;
|
||||||
|
}
|
||||||
|
LIST_REMOVE(sub, entries);
|
||||||
|
free(sub);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* notifies all subscriber of a number of port mapping change
|
||||||
|
* or external ip address change */
|
||||||
|
void
|
||||||
|
upnp_event_var_change_notify(enum subscriber_service_enum service)
|
||||||
|
{
|
||||||
|
struct subscriber * sub;
|
||||||
|
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
|
||||||
|
if(sub->service == service && sub->notify == NULL)
|
||||||
|
upnp_event_create_notify(sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create and add the notify object to the list */
|
||||||
|
static void
|
||||||
|
upnp_event_create_notify(struct subscriber * sub)
|
||||||
|
{
|
||||||
|
struct upnp_event_notify * obj;
|
||||||
|
int flags;
|
||||||
|
obj = calloc(1, sizeof(struct upnp_event_notify));
|
||||||
|
if(!obj) {
|
||||||
|
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) {
|
||||||
|
syslog(LOG_ERR, "%s: socket(): %m", "upnp_event_create_notify");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if((flags = fcntl(obj->s, F_GETFL, 0)) < 0) {
|
||||||
|
syslog(LOG_ERR, "%s: fcntl(..F_GETFL..): %m",
|
||||||
|
"upnp_event_create_notify");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if(fcntl(obj->s, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||||
|
syslog(LOG_ERR, "%s: fcntl(..F_SETFL..): %m",
|
||||||
|
"upnp_event_create_notify");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if(sub)
|
||||||
|
sub->notify = obj;
|
||||||
|
LIST_INSERT_HEAD(¬ifylist, obj, entries);
|
||||||
|
return;
|
||||||
|
error:
|
||||||
|
if(obj->s >= 0)
|
||||||
|
close(obj->s);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
upnp_event_notify_connect(struct upnp_event_notify * obj)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const char * p;
|
||||||
|
unsigned short port;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
if(!obj)
|
||||||
|
return;
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
i = 0;
|
||||||
|
if(obj->sub == NULL) {
|
||||||
|
obj->state = EError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p = obj->sub->callback;
|
||||||
|
p += 7; /* http:// */
|
||||||
|
while(*p != '/' && *p != ':')
|
||||||
|
obj->addrstr[i++] = *(p++);
|
||||||
|
obj->addrstr[i] = '\0';
|
||||||
|
if(*p == ':') {
|
||||||
|
obj->portstr[0] = *p;
|
||||||
|
i = 1;
|
||||||
|
p++;
|
||||||
|
port = (unsigned short)atoi(p);
|
||||||
|
while(*p != '/') {
|
||||||
|
if(i<7) obj->portstr[i++] = *p;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
obj->portstr[i] = 0;
|
||||||
|
} else {
|
||||||
|
port = 80;
|
||||||
|
obj->portstr[0] = '\0';
|
||||||
|
}
|
||||||
|
obj->path = p;
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
inet_aton(obj->addrstr, &addr.sin_addr);
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
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) {
|
||||||
|
syslog(LOG_ERR, "%s: connect(): %m", "upnp_event_notify_connect");
|
||||||
|
obj->state = EError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void upnp_event_prepare(struct upnp_event_notify * obj)
|
||||||
|
{
|
||||||
|
static const char notifymsg[] =
|
||||||
|
"NOTIFY %s HTTP/1.1\r\n"
|
||||||
|
"Host: %s%s\r\n"
|
||||||
|
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
||||||
|
"Content-Length: %d\r\n"
|
||||||
|
"NT: upnp:event\r\n"
|
||||||
|
"NTS: upnp:propchange\r\n"
|
||||||
|
"SID: %s\r\n"
|
||||||
|
"SEQ: %u\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"Cache-Control: no-cache\r\n"
|
||||||
|
"\r\n"
|
||||||
|
"%.*s\r\n";
|
||||||
|
char * xml;
|
||||||
|
int l;
|
||||||
|
if(obj->sub == NULL) {
|
||||||
|
obj->state = EError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch(obj->sub->service) {
|
||||||
|
case EContentDirectory:
|
||||||
|
xml = getVarsContentDirectory(&l);
|
||||||
|
break;
|
||||||
|
case EConnectionManager:
|
||||||
|
xml = getVarsConnectionManager(&l);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
xml = NULL;
|
||||||
|
l = 0;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
if(xml) {
|
||||||
|
free(xml);
|
||||||
|
xml = NULL;
|
||||||
|
}
|
||||||
|
//DEBUG printf("Preparing buffer:\n%s\n", obj->buffer);
|
||||||
|
obj->state = ESending;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void upnp_event_send(struct upnp_event_notify * obj)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0);
|
||||||
|
if(i<0) {
|
||||||
|
syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send");
|
||||||
|
obj->state = EError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(i != (obj->tosend - obj->sent))
|
||||||
|
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)
|
||||||
|
obj->state = EWaitingForResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void upnp_event_recv(struct upnp_event_notify * obj)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
n = recv(obj->s, obj->buffer, obj->buffersize, 0);
|
||||||
|
if(n<0) {
|
||||||
|
syslog(LOG_ERR, "%s: recv(): %m", "upnp_event_recv");
|
||||||
|
obj->state = EError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
syslog(LOG_DEBUG, "%s: (%dbytes) %.*s", "upnp_event_recv",
|
||||||
|
n, n, obj->buffer);
|
||||||
|
obj->state = EFinished;
|
||||||
|
if(obj->sub)
|
||||||
|
obj->sub->seq++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
upnp_event_process_notify(struct upnp_event_notify * obj)
|
||||||
|
{
|
||||||
|
switch(obj->state) {
|
||||||
|
case EConnecting:
|
||||||
|
/* now connected or failed to connect */
|
||||||
|
upnp_event_prepare(obj);
|
||||||
|
upnp_event_send(obj);
|
||||||
|
break;
|
||||||
|
case ESending:
|
||||||
|
upnp_event_send(obj);
|
||||||
|
break;
|
||||||
|
case EWaitingForResponse:
|
||||||
|
upnp_event_recv(obj);
|
||||||
|
break;
|
||||||
|
case EFinished:
|
||||||
|
close(obj->s);
|
||||||
|
obj->s = -1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
syslog(LOG_ERR, "upnp_event_process_notify: unknown state");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
syslog(LOG_DEBUG, "upnpevents_selectfds: %p %d %d",
|
||||||
|
obj, obj->state, obj->s);
|
||||||
|
if(obj->s >= 0) {
|
||||||
|
switch(obj->state) {
|
||||||
|
case ECreated:
|
||||||
|
upnp_event_notify_connect(obj);
|
||||||
|
if(obj->state != EConnecting)
|
||||||
|
break;
|
||||||
|
case EConnecting:
|
||||||
|
case ESending:
|
||||||
|
FD_SET(obj->s, writeset);
|
||||||
|
if(obj->s > *max_fd)
|
||||||
|
*max_fd = obj->s;
|
||||||
|
break;
|
||||||
|
case EFinished:
|
||||||
|
case EError:
|
||||||
|
case EWaitingForResponse:
|
||||||
|
FD_SET(obj->s, readset);
|
||||||
|
if(obj->s > *max_fd)
|
||||||
|
*max_fd = obj->s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnpevents_processfds(fd_set *readset, fd_set *writeset)
|
||||||
|
{
|
||||||
|
struct upnp_event_notify * obj;
|
||||||
|
struct upnp_event_notify * next;
|
||||||
|
struct subscriber * sub;
|
||||||
|
struct subscriber * subnext;
|
||||||
|
time_t curtime;
|
||||||
|
for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
|
||||||
|
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) {
|
||||||
|
if(FD_ISSET(obj->s, readset) || FD_ISSET(obj->s, writeset))
|
||||||
|
upnp_event_process_notify(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj = notifylist.lh_first;
|
||||||
|
while(obj != NULL) {
|
||||||
|
next = obj->entries.le_next;
|
||||||
|
if(obj->state == EError || obj->state == EFinished) {
|
||||||
|
if(obj->s >= 0) {
|
||||||
|
close(obj->s);
|
||||||
|
}
|
||||||
|
if(obj->sub)
|
||||||
|
obj->sub->notify = NULL;
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
if(obj->buffer) {
|
||||||
|
free(obj->buffer);
|
||||||
|
}
|
||||||
|
LIST_REMOVE(obj, entries);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
obj = next;
|
||||||
|
}
|
||||||
|
/* remove timeouted subscribers */
|
||||||
|
curtime = time(NULL);
|
||||||
|
for(sub = subscriberlist.lh_first; sub != NULL; ) {
|
||||||
|
subnext = sub->entries.le_next;
|
||||||
|
if(sub->timeout && curtime > sub->timeout && sub->notify == NULL) {
|
||||||
|
LIST_REMOVE(sub, entries);
|
||||||
|
free(sub);
|
||||||
|
}
|
||||||
|
sub = subnext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_MINIUPNPDCTL
|
||||||
|
void write_events_details(int s) {
|
||||||
|
int n;
|
||||||
|
char buff[80];
|
||||||
|
struct upnp_event_notify * obj;
|
||||||
|
struct subscriber * sub;
|
||||||
|
write(s, "Events details\n", 15);
|
||||||
|
for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
|
||||||
|
n = snprintf(buff, sizeof(buff), " %p sub=%p state=%d s=%d\n",
|
||||||
|
obj, obj->sub, obj->state, obj->s);
|
||||||
|
write(s, buff, n);
|
||||||
|
}
|
||||||
|
write(s, "Subscribers :\n", 14);
|
||||||
|
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
|
||||||
|
n = snprintf(buff, sizeof(buff), " %p timeout=%d seq=%u service=%d\n",
|
||||||
|
sub, sub->timeout, sub->seq, sub->service);
|
||||||
|
write(s, buff, n);
|
||||||
|
n = snprintf(buff, sizeof(buff), " notify=%p %s\n",
|
||||||
|
sub->notify, sub->uuid);
|
||||||
|
write(s, buff, n);
|
||||||
|
n = snprintf(buff, sizeof(buff), " %s\n",
|
||||||
|
sub->callback);
|
||||||
|
write(s, buff, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
40
upnpevents.h
Normal file
40
upnpevents.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/* 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 {
|
||||||
|
EWanCFG = 1,
|
||||||
|
EContentDirectory,
|
||||||
|
EConnectionManager,
|
||||||
|
EWanIPC,
|
||||||
|
EL3F
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
upnp_event_var_change_notify(enum subscriber_service_enum service);
|
||||||
|
|
||||||
|
const char *
|
||||||
|
upnpevents_addSubscriber(const char * eventurl,
|
||||||
|
const char * callback, int callbacklen,
|
||||||
|
int timeout);
|
||||||
|
|
||||||
|
int
|
||||||
|
upnpevents_removeSubscriber(const char * sid, int sidlen);
|
||||||
|
|
||||||
|
int
|
||||||
|
renewSubscription(const char * sid, int sidlen, int timeout);
|
||||||
|
|
||||||
|
void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd);
|
||||||
|
void upnpevents_processfds(fd_set *readset, fd_set *writeset);
|
||||||
|
|
||||||
|
#ifdef USE_MINIUPNPDCTL
|
||||||
|
void write_events_details(int s);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
81
upnpglobalvars.c
Normal file
81
upnpglobalvars.c
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/* $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 "config.h"
|
||||||
|
#include "upnpglobalvars.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;
|
||||||
|
|
||||||
|
#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 serialnumber[SERIALNUMBER_MAX_LEN] = "00000000";
|
||||||
|
|
||||||
|
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 media_dir[256];
|
95
upnpglobalvars.h
Normal file
95
upnpglobalvars.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/* 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 "miniupnpdtypes.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
/* name of the network interface used to acces internet */
|
||||||
|
extern const char * ext_if_name;
|
||||||
|
|
||||||
|
/* file to store all leases */
|
||||||
|
#ifdef ENABLE_LEASEFILE
|
||||||
|
extern const char * lease_file;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* forced ip address to use for this interface
|
||||||
|
* when NULL, getifaddr() is used */
|
||||||
|
extern const char * use_ext_ip_addr;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* runtime boolean flags */
|
||||||
|
extern int runtime_flags;
|
||||||
|
#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
|
||||||
|
#define CLEARFLAG(mask) runtime_flags &= ~mask
|
||||||
|
|
||||||
|
extern const char * pidfilename;
|
||||||
|
|
||||||
|
extern char uuidvalue[];
|
||||||
|
|
||||||
|
#define SERIALNUMBER_MAX_LEN (10)
|
||||||
|
extern char serialnumber[];
|
||||||
|
|
||||||
|
#define MODELNUMBER_MAX_LEN (48)
|
||||||
|
extern char modelnumber[];
|
||||||
|
|
||||||
|
#define PRESENTATIONURL_MAX_LEN (64)
|
||||||
|
extern char presentationurl[];
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
/* MAX_LAN_ADDR : maximum number of interfaces
|
||||||
|
* to listen to SSDP traffic */
|
||||||
|
#define MAX_LAN_ADDR (4)
|
||||||
|
extern int n_lan_addr;
|
||||||
|
extern struct lan_addr_s lan_addr[];
|
||||||
|
|
||||||
|
/* UPnP-A/V [DLNA] */
|
||||||
|
extern sqlite3 *db;
|
||||||
|
#define MEDIADIR_MAX_LEN (256)
|
||||||
|
extern char media_dir[];
|
||||||
|
|
||||||
|
#endif
|
1226
upnphttp.c
Normal file
1226
upnphttp.c
Normal file
File diff suppressed because it is too large
Load Diff
133
upnphttp.h
Normal file
133
upnphttp.h
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/* 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 __UPNPHTTP_H__
|
||||||
|
#define __UPNPHTTP_H__
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
/* server: HTTP header returned in all HTTP responses : */
|
||||||
|
#define MINIUPNPD_SERVER_STRING OS_VERSION " UPnP/1.0 miniupnpd/1.0"
|
||||||
|
|
||||||
|
/*
|
||||||
|
states :
|
||||||
|
0 - waiting for data to read
|
||||||
|
1 - waiting for HTTP Post Content.
|
||||||
|
...
|
||||||
|
>= 100 - to be deleted
|
||||||
|
*/
|
||||||
|
enum httpCommands {
|
||||||
|
EUnknown = 0,
|
||||||
|
EGet,
|
||||||
|
EPost,
|
||||||
|
EHead,
|
||||||
|
ESubscribe,
|
||||||
|
EUnSubscribe
|
||||||
|
};
|
||||||
|
|
||||||
|
struct upnphttp {
|
||||||
|
int socket;
|
||||||
|
struct in_addr clientaddr; /* client address */
|
||||||
|
int state;
|
||||||
|
char HttpVer[16];
|
||||||
|
/* request */
|
||||||
|
char * req_buf;
|
||||||
|
int req_buflen;
|
||||||
|
int req_contentlen;
|
||||||
|
int req_contentoff; /* header length */
|
||||||
|
enum httpCommands req_command;
|
||||||
|
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;
|
||||||
|
#endif
|
||||||
|
long long int req_RangeStart;
|
||||||
|
long long int req_RangeEnd;
|
||||||
|
long int req_chunklen;
|
||||||
|
int reqflags;
|
||||||
|
int respflags;
|
||||||
|
/* response */
|
||||||
|
char * res_buf;
|
||||||
|
int res_buflen;
|
||||||
|
int res_buf_alloclen;
|
||||||
|
/*int res_contentlen;*/
|
||||||
|
/*int res_contentoff;*/ /* header length */
|
||||||
|
LIST_ENTRY(upnphttp) entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
#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 *
|
||||||
|
New_upnphttp(int);
|
||||||
|
|
||||||
|
/* CloseSocket_upnphttp() */
|
||||||
|
void
|
||||||
|
CloseSocket_upnphttp(struct upnphttp *);
|
||||||
|
|
||||||
|
/* Delete_upnphttp() */
|
||||||
|
void
|
||||||
|
Delete_upnphttp(struct upnphttp *);
|
||||||
|
|
||||||
|
/* Process_upnphttp() */
|
||||||
|
void
|
||||||
|
Process_upnphttp(struct upnphttp *);
|
||||||
|
|
||||||
|
/* BuildHeader_upnphttp()
|
||||||
|
* build the header for the HTTP Response
|
||||||
|
* also allocate the buffer for body data */
|
||||||
|
void
|
||||||
|
BuildHeader_upnphttp(struct upnphttp * h, int respcode,
|
||||||
|
const char * respmsg,
|
||||||
|
int bodylen);
|
||||||
|
|
||||||
|
/* BuildResp_upnphttp()
|
||||||
|
* fill the res_buf buffer with the complete
|
||||||
|
* HTTP 200 OK response from the body passed as argument */
|
||||||
|
void
|
||||||
|
BuildResp_upnphttp(struct upnphttp *, const char *, int);
|
||||||
|
|
||||||
|
/* BuildResp2_upnphttp()
|
||||||
|
* same but with given response code/message */
|
||||||
|
void
|
||||||
|
BuildResp2_upnphttp(struct upnphttp * h, int respcode,
|
||||||
|
const char * respmsg,
|
||||||
|
const char * body, int bodylen);
|
||||||
|
|
||||||
|
/* SendResp_upnphttp() */
|
||||||
|
void
|
||||||
|
SendResp_upnphttp(struct upnphttp *);
|
||||||
|
|
||||||
|
void
|
||||||
|
SendResp_resizedimg(struct upnphttp *, char * url);
|
||||||
|
void
|
||||||
|
SendResp_thumbnail(struct upnphttp *, char * url);
|
||||||
|
/* SendResp_dlnafile()
|
||||||
|
* send the actual file data for a UPnP-A/V or DLNA request. */
|
||||||
|
void
|
||||||
|
SendResp_dlnafile(struct upnphttp *, char * url);
|
||||||
|
#endif
|
||||||
|
|
127
upnpreplyparse.c
Normal file
127
upnpreplyparse.c
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/* $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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "upnpreplyparse.h"
|
||||||
|
#include "minixml.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
NameValueParserStartElt(void * d, const char * name, int l)
|
||||||
|
{
|
||||||
|
struct NameValueParserData * data = (struct NameValueParserData *)d;
|
||||||
|
if(l>511)
|
||||||
|
l = 511;
|
||||||
|
memcpy(data->curelt, name, l);
|
||||||
|
data->curelt[l] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
NameValueParserGetData(void * d, const char * datas, int l)
|
||||||
|
{
|
||||||
|
struct NameValueParserData * data = (struct NameValueParserData *)d;
|
||||||
|
struct NameValue * nv;
|
||||||
|
nv = malloc(sizeof(struct NameValue));
|
||||||
|
if(l>511)
|
||||||
|
l = 511;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ParseNameValue(const char * buffer, int bufsize,
|
||||||
|
struct NameValueParserData * data)
|
||||||
|
{
|
||||||
|
struct xmlparser parser;
|
||||||
|
LIST_INIT(&(data->head));
|
||||||
|
/* init xmlparser object */
|
||||||
|
parser.xmlstart = buffer;
|
||||||
|
parser.xmlsize = bufsize;
|
||||||
|
parser.data = data;
|
||||||
|
parser.starteltfunc = NameValueParserStartElt;
|
||||||
|
parser.endeltfunc = 0;
|
||||||
|
parser.datafunc = NameValueParserGetData;
|
||||||
|
parser.attfunc = 0;
|
||||||
|
parsexml(&parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearNameValueList(struct NameValueParserData * pdata)
|
||||||
|
{
|
||||||
|
struct NameValue * nv;
|
||||||
|
while((nv = pdata->head.lh_first) != NULL)
|
||||||
|
{
|
||||||
|
LIST_REMOVE(nv, entries);
|
||||||
|
free(nv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
GetValueFromNameValueList(struct NameValueParserData * pdata,
|
||||||
|
const char * Name)
|
||||||
|
{
|
||||||
|
struct NameValue * nv;
|
||||||
|
char * p = NULL;
|
||||||
|
for(nv = pdata->head.lh_first;
|
||||||
|
(nv != NULL) && (p == NULL);
|
||||||
|
nv = nv->entries.le_next)
|
||||||
|
{
|
||||||
|
if(strcmp(nv->name, Name) == 0)
|
||||||
|
p = nv->value;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
void
|
||||||
|
DisplayNameValueList(char * buffer, int bufsize)
|
||||||
|
{
|
||||||
|
struct NameValueParserData pdata;
|
||||||
|
struct NameValue * nv;
|
||||||
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
|
for(nv = pdata.head.lh_first;
|
||||||
|
nv != NULL;
|
||||||
|
nv = nv->entries.le_next)
|
||||||
|
{
|
||||||
|
printf("%s = %s\n", nv->name, nv->value);
|
||||||
|
}
|
||||||
|
ClearNameValueList(&pdata);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
57
upnpreplyparse.h
Normal file
57
upnpreplyparse.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/* 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 __UPNPREPLYPARSE_H__
|
||||||
|
#define __UPNPREPLYPARSE_H__
|
||||||
|
|
||||||
|
#include <sys/queue.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct NameValue {
|
||||||
|
LIST_ENTRY(NameValue) entries;
|
||||||
|
char name[64];
|
||||||
|
char value[512];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameValueParserData {
|
||||||
|
LIST_HEAD(listhead, NameValue) head;
|
||||||
|
char curelt[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ParseNameValue() */
|
||||||
|
void
|
||||||
|
ParseNameValue(const char * buffer, int bufsize,
|
||||||
|
struct NameValueParserData * data);
|
||||||
|
|
||||||
|
/* ClearNameValueList() */
|
||||||
|
void
|
||||||
|
ClearNameValueList(struct NameValueParserData * pdata);
|
||||||
|
|
||||||
|
/* GetValueFromNameValueList() */
|
||||||
|
char *
|
||||||
|
GetValueFromNameValueList(struct NameValueParserData * pdata,
|
||||||
|
const char * Name);
|
||||||
|
|
||||||
|
/* GetValueFromNameValueListIgnoreNS() */
|
||||||
|
char *
|
||||||
|
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
|
||||||
|
const char * Name);
|
||||||
|
|
||||||
|
/* DisplayNameValueList() */
|
||||||
|
#ifdef DEBUG
|
||||||
|
void
|
||||||
|
DisplayNameValueList(char * buffer, int bufsize);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
787
upnpsoap.c
Normal file
787
upnpsoap.c
Normal file
@ -0,0 +1,787 @@
|
|||||||
|
/* $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 <sys/socket.h>
|
||||||
|
#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 "config.h"
|
||||||
|
#include "upnpglobalvars.h"
|
||||||
|
#include "upnphttp.h"
|
||||||
|
#include "upnpsoap.h"
|
||||||
|
#include "upnpreplyparse.h"
|
||||||
|
#include "getifaddr.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\"?>\r\n"
|
||||||
|
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
||||||
|
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||||
|
"<s:Body>";
|
||||||
|
|
||||||
|
static const char afterbody[] =
|
||||||
|
"</s:Body>"
|
||||||
|
"</s:Envelope>\r\n";
|
||||||
|
|
||||||
|
BuildHeader_upnphttp(h, 200, "OK", sizeof(beforebody) - 1
|
||||||
|
+ sizeof(afterbody) - 1 + bodylen );
|
||||||
|
|
||||||
|
memcpy(h->res_buf + h->res_buflen, beforebody, sizeof(beforebody) - 1);
|
||||||
|
h->res_buflen += sizeof(beforebody) - 1;
|
||||||
|
|
||||||
|
memcpy(h->res_buf + h->res_buflen, body, bodylen);
|
||||||
|
h->res_buflen += bodylen;
|
||||||
|
|
||||||
|
memcpy(h->res_buf + h->res_buflen, afterbody, sizeof(afterbody) - 1);
|
||||||
|
h->res_buflen += sizeof(afterbody) - 1;
|
||||||
|
|
||||||
|
SendResp_upnphttp(h);
|
||||||
|
CloseSocket_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[] =
|
||||||
|
"<u:%sResponse "
|
||||||
|
"xmlns:u=\"%s\">"
|
||||||
|
"<Id>%d</Id>"
|
||||||
|
"</u:%sResponse>";
|
||||||
|
|
||||||
|
char body[512];
|
||||||
|
int bodylen;
|
||||||
|
|
||||||
|
bodylen = snprintf(body, sizeof(body), resp,
|
||||||
|
action, "urn:schemas-upnp-org:service:ContentDirectory:1",
|
||||||
|
1, action);
|
||||||
|
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
IsAuthorizedValidated(struct upnphttp * h, const char * action)
|
||||||
|
{
|
||||||
|
static const char resp[] =
|
||||||
|
"<u:%sResponse "
|
||||||
|
"xmlns:u=\"%s\">"
|
||||||
|
"<Result>%d</Result>"
|
||||||
|
"</u:%sResponse>";
|
||||||
|
|
||||||
|
char body[512];
|
||||||
|
int bodylen;
|
||||||
|
|
||||||
|
bodylen = snprintf(body, sizeof(body), resp,
|
||||||
|
action, "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1",
|
||||||
|
1, action);
|
||||||
|
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
GetProtocolInfo(struct upnphttp * h, const char * action)
|
||||||
|
{
|
||||||
|
static const char resp[] =
|
||||||
|
"<u:%sResponse "
|
||||||
|
"xmlns:u=\"%s\">"
|
||||||
|
"<Source>"
|
||||||
|
/*"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[1536];
|
||||||
|
int bodylen;
|
||||||
|
|
||||||
|
bodylen = snprintf(body, sizeof(body), resp,
|
||||||
|
action, "urn:schemas-upnp-org:service:ConnectionManager:1",
|
||||||
|
action);
|
||||||
|
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
GetSortCapabilities(struct upnphttp * h, const char * action)
|
||||||
|
{
|
||||||
|
static const char resp[] =
|
||||||
|
"<u:%sResponse "
|
||||||
|
"xmlns:u=\"%s\">"
|
||||||
|
"<SortCaps></SortCaps>"
|
||||||
|
"</u:%sResponse>";
|
||||||
|
|
||||||
|
char body[512];
|
||||||
|
int bodylen;
|
||||||
|
|
||||||
|
bodylen = snprintf(body, sizeof(body), resp,
|
||||||
|
action, "urn:schemas-upnp-org:service:ContentDirectory:1",
|
||||||
|
action);
|
||||||
|
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
GetSearchCapabilities(struct upnphttp * h, const char * action)
|
||||||
|
{
|
||||||
|
static const char resp[] =
|
||||||
|
"<u:%sResponse "
|
||||||
|
"xmlns:u=\"%s\">"
|
||||||
|
"<SearchCaps>dc:title,dc:creator,upnp:class,upnp:artist,upnp:album,@refID</SearchCaps>"
|
||||||
|
"</u:%sResponse>";
|
||||||
|
|
||||||
|
char body[512];
|
||||||
|
int bodylen;
|
||||||
|
|
||||||
|
bodylen = snprintf(body, sizeof(body), resp,
|
||||||
|
action, "urn:schemas-upnp-org:service:ContentDirectory:1",
|
||||||
|
action);
|
||||||
|
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
GetCurrentConnectionIDs(struct upnphttp * h, const char * action)
|
||||||
|
{
|
||||||
|
/* TODO: Use real data. - JM */
|
||||||
|
static const char resp[] =
|
||||||
|
"<u:%sResponse "
|
||||||
|
"xmlns:u=\"%s\">"
|
||||||
|
"<ConnectionIDs>-1</ConnectionIDs>"
|
||||||
|
"</u:%sResponse>";
|
||||||
|
|
||||||
|
char body[512];
|
||||||
|
int bodylen;
|
||||||
|
|
||||||
|
bodylen = snprintf(body, sizeof(body), resp,
|
||||||
|
action, "urn:schemas-upnp-org:service:ConnectionManager:1",
|
||||||
|
action);
|
||||||
|
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
|
||||||
|
{
|
||||||
|
/* TODO: Use real data. - JM */
|
||||||
|
static const char resp[] =
|
||||||
|
"<u:%sResponse "
|
||||||
|
"xmlns:u=\"%s\">"
|
||||||
|
"<RcsID>-1</RcsID>"
|
||||||
|
"<AVTransportID>-1</AVTransportID>"
|
||||||
|
"<ProtocolInfo>"
|
||||||
|
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN,"
|
||||||
|
"</ProtocolInfo>"
|
||||||
|
"<PeerConnectionManager>0</PeerConnectionManager>"
|
||||||
|
"<PeerConnectionID>-1</PeerConnectionID>"
|
||||||
|
"<Direction>0</Direction>"
|
||||||
|
"<Status>0</Status>"
|
||||||
|
"</u:%sResponse>";
|
||||||
|
|
||||||
|
char body[sizeof(resp)+128];
|
||||||
|
int bodylen;
|
||||||
|
|
||||||
|
bodylen = snprintf(body, sizeof(body), resp,
|
||||||
|
action, "urn:schemas-upnp-org:service:ConnectionManager:1",
|
||||||
|
action);
|
||||||
|
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int callback(void *args, int argc, char **argv, char **azColName)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
passed_args->total++;
|
||||||
|
|
||||||
|
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
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
sprintf(str_buf, "<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, ">"
|
||||||
|
"<dc:title>%s</dc:title>"
|
||||||
|
"<upnp:class>object.%s</upnp:class>",
|
||||||
|
title?title:name, class);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
if( comment && (!passed_args->filter || strstr(passed_args->filter, "dc:description")) ) {
|
||||||
|
sprintf(str_buf, "<dc:description>%s</dc:description>", comment);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) {
|
||||||
|
sprintf(str_buf, "<dc:creator>%s</dc:creator>", creator);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( date && (!passed_args->filter || strstr(passed_args->filter, "dc:date")) ) {
|
||||||
|
sprintf(str_buf, "<dc:date>%s</dc:date>", date);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( artist && (!passed_args->filter || strstr(passed_args->filter, "upnp:artist")) ) {
|
||||||
|
sprintf(str_buf, "<upnp:artist>%s</upnp:artist>", artist);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( album && (!passed_args->filter || strstr(passed_args->filter, "upnp:album")) ) {
|
||||||
|
sprintf(str_buf, "<upnp:album>%s</upnp:album>", album);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( genre && (!passed_args->filter || strstr(passed_args->filter, "upnp:genre")) ) {
|
||||||
|
sprintf(str_buf, "<upnp:genre>%s</upnp:genre>", genre);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( track && atoi(track) && (!passed_args->filter || strstr(passed_args->filter, "upnp:originalTrackNumber")) ) {
|
||||||
|
sprintf(str_buf, "<upnp:originalTrackNumber>%s</upnp:originalTrackNumber>", track);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( !passed_args->filter || strstr(passed_args->filter, "res") ) {
|
||||||
|
strcat(passed_args->resp, "<res ");
|
||||||
|
if( size && (!passed_args->filter || strstr(passed_args->filter, "res@size")) ) {
|
||||||
|
sprintf(str_buf, "size=\"%s\" ", size);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( duration && (!passed_args->filter || strstr(passed_args->filter, "res@duration")) ) {
|
||||||
|
sprintf(str_buf, "duration=\"%s\" ", duration);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( bitrate && (!passed_args->filter || strstr(passed_args->filter, "res@bitrate")) ) {
|
||||||
|
sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( sampleFrequency && (!passed_args->filter || strstr(passed_args->filter, "res@sampleFrequency")) ) {
|
||||||
|
sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
if( nrAudioChannels && (!passed_args->filter || strstr(passed_args->filter, "res@nrAudioChannels")) ) {
|
||||||
|
sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\">"
|
||||||
|
"http://%s:5555/MediaItems/%s"
|
||||||
|
"</res>",
|
||||||
|
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, "<res "
|
||||||
|
"protocolInfo=\"http-get:*:%s:%s\">"
|
||||||
|
"http://%s:5555/Resized/%s"
|
||||||
|
"</res>",
|
||||||
|
mime, "DLNA.ORG_PN=JPEG_SM", lan_addr[0].str, id);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if( tn && atoi(tn) && dlna_pn ) {
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
strcat(passed_args->resp, "<res ");
|
||||||
|
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\">"
|
||||||
|
"http://%s:5555/Thumbnails/%s"
|
||||||
|
"</res>",
|
||||||
|
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, id);
|
||||||
|
}
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
}
|
||||||
|
strcpy(str_buf, "</item>");
|
||||||
|
}
|
||||||
|
else if( strncmp(class, "container", 9) == 0 )
|
||||||
|
{
|
||||||
|
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, "<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, ">"
|
||||||
|
"<dc:title>%s</dc:title>"
|
||||||
|
"<upnp:class>object.%s</upnp:class>"
|
||||||
|
"</container>",
|
||||||
|
name, class);
|
||||||
|
sqlite3_free_table(result);
|
||||||
|
}
|
||||||
|
strcat(passed_args->resp, str_buf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
BrowseContentDirectory(struct upnphttp * h, const char * action)
|
||||||
|
{
|
||||||
|
static const char resp0[] =
|
||||||
|
"<u:BrowseResponse "
|
||||||
|
"xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">"
|
||||||
|
"<Result>"
|
||||||
|
"<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/\">\n";
|
||||||
|
static const char resp1[] = "</DIDL-Lite></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;
|
||||||
|
int ret;
|
||||||
|
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");
|
||||||
|
if( !ObjectId )
|
||||||
|
ObjectId = GetValueFromNameValueList(&data, "ContainerID");
|
||||||
|
|
||||||
|
memset(&args, 0, sizeof(args));
|
||||||
|
args.total = 0;
|
||||||
|
args.returned = 0;
|
||||||
|
args.requested = RequestedCount;
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
ClearNameValueList(&data);
|
||||||
|
SoapError(h, 402, "Invalid Args");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if( strlen(Filter) > 1 )
|
||||||
|
args.filter = Filter;
|
||||||
|
|
||||||
|
args.resp = resp;
|
||||||
|
if( strcmp(BrowseFlag, "BrowseMetadata") == 0 )
|
||||||
|
{
|
||||||
|
args.requested = 1;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
if( ret != SQLITE_OK ){
|
||||||
|
printf("SQL error: %s\n", zErrMsg);
|
||||||
|
sqlite3_free(zErrMsg);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
SearchContentDirectory(struct upnphttp * h, const char * action)
|
||||||
|
{
|
||||||
|
static const char resp0[] =
|
||||||
|
"<u:SearchResponse "
|
||||||
|
"xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">"
|
||||||
|
"<Result>"
|
||||||
|
"<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/\">\n";
|
||||||
|
static const char resp1[] = "</DIDL-Lite></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");
|
||||||
|
|
||||||
|
memset(&args, 0, sizeof(args));
|
||||||
|
args.total = 0;
|
||||||
|
args.returned = 0;
|
||||||
|
args.requested = RequestedCount;
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
ClearNameValueList(&data);
|
||||||
|
SoapError(h, 402, "Invalid Args");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if( strlen(Filter) > 1 )
|
||||||
|
args.filter = Filter;
|
||||||
|
if( strcmp(ContainerID, "0") == 0 )
|
||||||
|
*ContainerID = '%';
|
||||||
|
if( !SearchCriteria )
|
||||||
|
{
|
||||||
|
asprintf(&SearchCriteria, "1 = 1");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SearchCriteria = modifyString(SearchCriteria, """, "\"", 0);
|
||||||
|
SearchCriteria = modifyString(SearchCriteria, "'", "'", 0);
|
||||||
|
SearchCriteria = modifyString(SearchCriteria, "derivedfrom", "like", 1);
|
||||||
|
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:artist", "d.ARTIST", 0);
|
||||||
|
SearchCriteria = modifyString(SearchCriteria, "upnp:album", "d.ALBUM", 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);
|
||||||
|
SearchCriteria = modifyString(SearchCriteria, "object.", "", 0);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
strncpy(ext_ip_addr, use_ext_ip_addr, INET_ADDRSTRLEN);
|
||||||
|
}
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
if( (h->clientaddr.s_addr & lan_addr[i].mask.s_addr)
|
||||||
|
== (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
|
||||||
|
{
|
||||||
|
strncpy(ext_ip_addr, lan_addr[i].ext_ip_str, INET_ADDRSTRLEN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bodylen = snprintf(body, sizeof(body), resp,
|
||||||
|
action, "urn:schemas-upnp-org:service:WANIPConnection:1",
|
||||||
|
ext_ip_addr, action);
|
||||||
|
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If a control point calls QueryStateVariable on a state variable that is not
|
||||||
|
buffered in memory within (or otherwise available from) the service,
|
||||||
|
the service must return a SOAP fault with an errorCode of 404 Invalid Var.
|
||||||
|
|
||||||
|
QueryStateVariable remains useful as a limited test tool but may not be
|
||||||
|
part of some future versions of UPnP.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
QueryStateVariable(struct upnphttp * h, const char * action)
|
||||||
|
{
|
||||||
|
static const char resp[] =
|
||||||
|
"<u:%sResponse "
|
||||||
|
"xmlns:u=\"%s\">"
|
||||||
|
"<return>%s</return>"
|
||||||
|
"</u:%sResponse>";
|
||||||
|
|
||||||
|
char body[512];
|
||||||
|
int bodylen;
|
||||||
|
struct NameValueParserData data;
|
||||||
|
const char * var_name;
|
||||||
|
|
||||||
|
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
|
||||||
|
/*var_name = GetValueFromNameValueList(&data, "QueryStateVariable"); */
|
||||||
|
/*var_name = GetValueFromNameValueListIgnoreNS(&data, "varName");*/
|
||||||
|
var_name = GetValueFromNameValueList(&data, "varName");
|
||||||
|
|
||||||
|
/*syslog(LOG_INFO, "QueryStateVariable(%.40s)", var_name); */
|
||||||
|
|
||||||
|
if(!var_name)
|
||||||
|
{
|
||||||
|
SoapError(h, 402, "Invalid Args");
|
||||||
|
}
|
||||||
|
else if(strcmp(var_name, "ConnectionStatus") == 0)
|
||||||
|
{
|
||||||
|
bodylen = snprintf(body, sizeof(body), resp,
|
||||||
|
action, "urn:schemas-upnp-org:control-1-0",
|
||||||
|
"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
|
||||||
|
{
|
||||||
|
syslog(LOG_NOTICE, "%s: Unknown: %s", action, var_name?var_name:"");
|
||||||
|
SoapError(h, 404, "Invalid Var");
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearNameValueList(&data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct
|
||||||
|
{
|
||||||
|
const char * methodName;
|
||||||
|
void (*methodImpl)(struct upnphttp *, const char *);
|
||||||
|
}
|
||||||
|
soapMethods[] =
|
||||||
|
{
|
||||||
|
{ "GetExternalIPAddress", GetExternalIPAddress},
|
||||||
|
{ "QueryStateVariable", QueryStateVariable},
|
||||||
|
{ "GetStatusInfo", GetStatusInfo},
|
||||||
|
{ "Browse", BrowseContentDirectory},
|
||||||
|
{ "Search", SearchContentDirectory},
|
||||||
|
{ "GetSearchCapabilities", GetSearchCapabilities},
|
||||||
|
{ "GetSortCapabilities", GetSortCapabilities},
|
||||||
|
{ "GetSystemUpdateID", GetSystemUpdateID},
|
||||||
|
{ "GetProtocolInfo", GetProtocolInfo},
|
||||||
|
{ "GetCurrentConnectionIDs", GetCurrentConnectionIDs},
|
||||||
|
{ "GetCurrentConnectionInfo", GetCurrentConnectionInfo},
|
||||||
|
{ "IsAuthorized", IsAuthorizedValidated},
|
||||||
|
{ "IsValidated", IsAuthorizedValidated},
|
||||||
|
{ 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
p++;
|
||||||
|
p2 = strchr(p, '"');
|
||||||
|
if(p2)
|
||||||
|
methodlen = p2 - p;
|
||||||
|
else
|
||||||
|
methodlen = n - (p - action);
|
||||||
|
/*syslog(LOG_DEBUG, "SoapMethod: %.*s", methodlen, p);*/
|
||||||
|
while(soapMethods[i].methodName)
|
||||||
|
{
|
||||||
|
len = strlen(soapMethods[i].methodName);
|
||||||
|
if(strncmp(p, soapMethods[i].methodName, len) == 0)
|
||||||
|
{
|
||||||
|
soapMethods[i].methodImpl(h, soapMethods[i].methodName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
syslog(LOG_NOTICE, "SoapMethod: Unknown: %.*s", methodlen, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
SoapError(h, 401, "Invalid Action");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Standard Errors:
|
||||||
|
*
|
||||||
|
* errorCode errorDescription Description
|
||||||
|
* -------- ---------------- -----------
|
||||||
|
* 401 Invalid Action No action by that name at this service.
|
||||||
|
* 402 Invalid Args Could be any of the following: not enough in args,
|
||||||
|
* too many in args, no in arg by that name,
|
||||||
|
* one or more in args are of the wrong data type.
|
||||||
|
* 403 Out of Sync Out of synchronization.
|
||||||
|
* 501 Action Failed May be returned in current state of service
|
||||||
|
* prevents invoking that action.
|
||||||
|
* 600-699 TBD Common action errors. Defined by UPnP Forum
|
||||||
|
* Technical Committee.
|
||||||
|
* 700-799 TBD Action-specific errors for standard actions.
|
||||||
|
* Defined by UPnP Forum working committee.
|
||||||
|
* 800-899 TBD Action-specific errors for non-standard actions.
|
||||||
|
* Defined by UPnP vendor.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
SoapError(struct upnphttp * h, int errCode, const char * errDesc)
|
||||||
|
{
|
||||||
|
static const char resp[] =
|
||||||
|
"<s:Envelope "
|
||||||
|
"xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
||||||
|
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||||
|
"<s:Body>"
|
||||||
|
"<s:Fault>"
|
||||||
|
"<faultcode>s:Client</faultcode>"
|
||||||
|
"<faultstring>UPnPError</faultstring>"
|
||||||
|
"<detail>"
|
||||||
|
"<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">"
|
||||||
|
"<errorCode>%d</errorCode>"
|
||||||
|
"<errorDescription>%s</errorDescription>"
|
||||||
|
"</UPnPError>"
|
||||||
|
"</detail>"
|
||||||
|
"</s:Fault>"
|
||||||
|
"</s:Body>"
|
||||||
|
"</s:Envelope>";
|
||||||
|
|
||||||
|
char body[2048];
|
||||||
|
int bodylen;
|
||||||
|
|
||||||
|
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);
|
||||||
|
CloseSocket_upnphttp(h);
|
||||||
|
}
|
||||||
|
|
22
upnpsoap.h
Normal file
22
upnpsoap.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* 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__
|
||||||
|
|
||||||
|
/* ExecuteSoapAction():
|
||||||
|
* this method executes the requested Soap Action */
|
||||||
|
void
|
||||||
|
ExecuteSoapAction(struct upnphttp *, const char *, int);
|
||||||
|
|
||||||
|
/* SoapError():
|
||||||
|
* sends a correct SOAP error with an UPNPError code and
|
||||||
|
* description */
|
||||||
|
void
|
||||||
|
SoapError(struct upnphttp * h, int errCode, const char * errDesc);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Reference in New Issue
Block a user