added | .gitignore |
removed | src |
removed | src/control |
removed | src/control/httpdownload.hxx |
removed | src/soaphelp.cxx |
removed | src/soaphelp.hxx |
removed | src/upnpp_p.hxx |
removed | src/upnpplib.cxx |
removed | src/upnpplib.hxx |
removed | src/upnpputils.hxx |
removed | src/workqueue.hxx |
changed | debian/changelog |
changed | debian/control |
changed | debian/copyright |
changed | debian/watch |
changed | rpm/upmpdcli.spec |
changed | Makefile.am |
changed | configure.ac |
copied | debian/postinst -> libupnpp/control/httpdownload.hxx |
copied | debian/upmpdcli.init -> libupnpp/soaphelp.hxx |
copied | src/control/#cdircontent.hxx# -> libupnpp/control/cdircontent.hxx |
copied | src/control/#cdirectory.cxx# -> libupnpp/control/cdirectory.cxx |
copied | src/control/#cdirectory.hxx# -> libupnpp/control/cdirectory.hxx |
copied | src/control/#discovery.cxx# -> libupnpp/control/discovery.cxx |
copied | src/control/#mediaserver.cxx# -> libupnpp/control/mediaserver.cxx |
src
File was removed.
src/control
File was removed.
src/control/httpdownload.hxx
File was removed.
src/soaphelp.cxx
File was removed.
src/soaphelp.hxx
File was removed.
src/upnpp_p.hxx
File was removed.
src/upnpplib.cxx
File was removed.
src/upnpplib.hxx
File was removed.
src/upnpputils.hxx
File was removed.
src/workqueue.hxx
File was removed.
debian/postinst to libupnpp/control/httpdownload.hxx
--- a/debian/postinst +++ b/libupnpp/control/httpdownload.hxx @@ -1,15 +1,9 @@ -#! /bin/sh -# postinst for upmpdcli +#ifndef _HTTPDOWNLOAD_H_X_INCLUDED_ +#define _HTTPDOWNLOAD_H_X_INCLUDED_ -set -e +#include <string> -if [ "$1" = configure ]; then - if ! getent passwd upmpdcli > /dev/null; then - adduser --disabled-password --quiet --system \ - --home /nonexistent --no-create-home --shell /bin/false upmpdcli - fi -fi +extern bool downloadUrlWithCurl(const std::string& url, + std::string& out, long timeoutsecs); -#DEBHELPER# - -exit 0 +#endif /* _HTTPDOWNLOAD.H_X_INCLUDED_ */
debian/upmpdcli.init to libupnpp/soaphelp.hxx
--- a/debian/upmpdcli.init +++ b/libupnpp/soaphelp.hxx @@ -1,149 +1,119 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: upmpdcli -# Required-Start: $network $local_fs $remote_fs -# Required-Stop: $remote_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: UPnP Renderer front-end to MPD -# Description: upmpdcli is as an UPnP Media Renderer front-end -# to MPD, and enables you to control it with any UPnP controller -# running, for example, on an Android tablet. It uses the MPD -# instance to actually play the tracks. A typical configuration -# would have for example, MPD running on a Raspberry PI, with -# upmpdcli on the same host or any other Linux PC on the network. -### END INIT INFO +/* Copyright (C) 2014 J.F.Dockes + * 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. + */ +#ifndef _SOAPHELP_H_X_INCLUDED_ +#define _SOAPHELP_H_X_INCLUDED_ -# Author: Jean-Francois Dockes <jf@dockes.org> +#include <upnp/ixml.h> // for IXML_Document -# PATH should only include /usr/* if it runs after the mountnfs.sh script -PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC=upmpdcli # Introduce a short description here -NAME=upmpdcli # Introduce the short server's name here -DAEMON=/usr/bin/upmpdcli # Introduce the server's location here -DAEMON_ARGS="-D -c /etc/upmpdcli.conf" # Arguments to run the daemon with -PIDFILE=/var/run/$NAME.pid -SCRIPTNAME=/etc/init.d/$NAME +#include <map> // for map +#include <string> // for string +#include <unordered_map> // for unordered_map +#include <utility> // for pair +#include <vector> // for vector -# Exit if the package is not installed -[ -x $DAEMON ] || exit 0 +namespace UPnPP { -# Read configuration variable file if it is present -[ -r /etc/default/$NAME ] && . /etc/default/$NAME +/** Store returned values after decoding the arguments in a SOAP Call */ +class SoapDecodeOutput { +public: + std::string name; + std::map<std::string, std::string> args; -# Load the VERBOSE setting and other rcS variables -. /lib/init/vars.sh + // Utility methods + bool getBool(const char *nm, bool *value) const; + bool getInt(const char *nm, int *value) const; + bool getString(const char *nm, std::string *value) const; + bool get(const char *nm, bool *value) const {return getBool(nm, value);} + bool get(const char *nm, int *value) const {return getInt(nm, value);} + bool get(const char *nm, std::string *value) const {return getString(nm, value);} +}; -# Define LSB log_* functions. -# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. -. /lib/lsb/init-functions +/** Decode the XML in a Soap call and return the arguments in a SoapArgs + * structure. + * + * @param name the action name is stored for convenience in the return + * structure. The caller normally gets it from libupnp, passing it is simpler + * than retrieving from the input top node where it has a namespace qualifier. + * @param actReq the XML document containing the SOAP data. + * @param[output] res the decoded data. + * @return true for success, false if a decoding error occurred. + */ +extern bool decodeSoapBody(const char *name, IXML_Document *actReq, + SoapDecodeOutput *res); -# -# Function that starts the daemon/service -# -do_start() -{ - # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ - || return 1 - start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ - $DAEMON_ARGS \ - || return 2 - # Add code here, if necessary, that waits for the process to be ready - # to handle requests from services started subsequently which depend - # on this one. As a last resort, sleep for some time. -} +/** Store the values to be encoded in a SOAP response. + * + * The elements in the response must be in a defined order, so we + * can't use a map as container, we use a vector of pairs instead. + * The generic UpnpDevice callback fills up name and service type, the + * device call only needs to fill the data vector. + */ +class SoapEncodeInput { +public: + SoapEncodeInput() {} + SoapEncodeInput(const std::string& st, const std::string& nm) + : serviceType(st), name(nm) {} + SoapEncodeInput& addarg(const std::string& k, const std::string& v) { + data.push_back(std::pair<std::string, std::string>(k, v)); + return *this; + } + SoapEncodeInput& operator() (const std::string& k, const std::string& v) { + data.push_back(std::pair<std::string, std::string>(k, v)); + return *this; + } + static std::string i2s(int val); + std::string serviceType; + std::string name; + std::vector<std::pair<std::string, std::string> > data; +}; -# -# Function that stops the daemon/service -# -do_stop() -{ - # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - start-stop-daemon --stop --quiet --retry=TERM/5/KILL/5 --pidfile $PIDFILE --name $NAME - RETVAL="$?" - [ "$RETVAL" = 2 ] && return 2 - # Many daemons don't delete their pidfiles when they exit. - rm -f $PIDFILE - return "$RETVAL" -} +// Until we can fix the device code. +typedef SoapEncodeInput SoapData; +typedef SoapDecodeOutput SoapArgs; -# -# Function that sends a SIGHUP to the daemon/service -# -#do_reload() { - # - # If the daemon can reload its configuration without - # restarting (for example, when it is sent a SIGHUP), - # then implement that here. - # -# start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME -# return 0 -#} +/** Build a SOAP response data XML document from a list of values */ +extern IXML_Document *buildSoapBody(const SoapEncodeInput& data, + bool isResp = true); -case "$1" in - start) - [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME" - do_start - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - stop) - [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" - do_stop - case "$?" in - 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; - 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; - esac - ;; - status) - status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? - ;; - #reload|force-reload) - # - # If do_reload() is not implemented then leave this commented out - # and leave 'force-reload' as an alias for 'restart'. - # - #log_daemon_msg "Reloading $DESC" "$NAME" - #do_reload - #log_end_msg $? - #;; - restart|force-reload) - # - # If the "reload" option is implemented then remove the - # 'force-reload' alias - # - log_daemon_msg "Restarting $DESC" "$NAME" - do_stop - case "$?" in - 0|1) - do_start - case "$?" in - 0) log_end_msg 0 ;; - 1) log_end_msg 1 ;; # Old process is still running - *) log_end_msg 1 ;; # Failed to start - esac - ;; - *) - # Failed to stop - log_end_msg 1 - ;; - esac - ;; - *) - #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 - echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 - exit 3 - ;; -esac +namespace SoapHelp { + std::string xmlQuote(const std::string& in); + std::string xmlUnquote(const std::string& in); + std::string i2s(int val); + inline std::string val2s(const std::string& val) {return val;} + inline std::string val2s(int val) {return i2s(val);} + inline std::string val2s(bool val) {return i2s(int(val));} +}; -: +/** Decode UPnP Event data. This is not soap, but it's quite close to + * the other code in here so whatever... + * + * The variable values are contained in a propertyset XML document: + * <?xml version="1.0"?> + * <e:propertyset xmlns:e="urn:schemas-upnp-org:event-1-0"> + * <e:property> + * <variableName>new value</variableName> + * </e:property> + * <!-- Other variable names and values (if any) go here. --> + * </e:propertyset> + */ +extern bool decodePropertySet(IXML_Document *doc, + std::unordered_map<std::string,std::string>& out); + + +} // namespace UPnPP + +#endif /* _SOAPHELP_H_X_INCLUDED_ */
src/control/#cdircontent.hxx# to libupnpp/control/cdircontent.hxx
--- a/src/control/#cdircontent.hxx# +++ b/libupnpp/control/cdircontent.hxx @@ -108,13 +108,15 @@ } - // Sim - std::string f2s(const std::string& nm, bool isres) { - string val; - if (isres) { - dop.getrprop(0, nm, val); + /** Simplified interface to retrieving values: we don't distinguish + * between non-existing and empty, and we only use the first ressource + */ + std::string f2s(const std::string& nm, bool isresfield) { + std::string val; + if (isresfield) { + getrprop(0, nm, val); } else { - dop.getprop(nm, val); + getprop(nm, val); } return val; }
src/control/#cdirectory.cxx# to libupnpp/control/cdirectory.cxx
--- a/src/control/#cdirectory.cxx# +++ b/libupnpp/control/cdirectory.cxx @@ -18,7 +18,9 @@ #include "libupnpp/control/cdirectory.hxx" -#include <regexp.h> +#include <sys/types.h> +#include <regex.h> + #include <upnp/upnp.h> // for UPNP_E_SUCCESS, etc #include <upnp/upnptools.h> // for UpnpGetErrorMessage @@ -44,15 +46,16 @@ const string ContentDirectory::SType("urn:schemas-upnp-org:service:ContentDirectory:1"); class SimpleRegexp { +public: SimpleRegexp(const string& exp, int flags) : m_ok(false) { - if (regcomp(exp.c_str(), &m_expr, flags) == 0) { + if (regcomp(&m_expr, exp.c_str(), flags) == 0) { m_ok = true; } } ~SimpleRegexp() { regfree(&m_expr); } - bool simpleMatch(const string& val) { + bool simpleMatch(const string& val) const { if (!ok()) return false; if (regexec(&m_expr, val.c_str(), 0, 0, 0) == 0) { @@ -61,11 +64,11 @@ return false; } } - bool operator() (const string& val) { + bool operator() (const string& val) const { return simpleMatch(val); } - bool ok() {return m_ok;} + bool ok() const {return m_ok;} private: bool m_ok; regex_t m_expr; @@ -78,11 +81,11 @@ manufacturer: PacketVideo model TwonkyMedia Server manufacturer: ? model MediaTomb */ -static const SimpleRegexp bubble_rx(".*bubble.*", REG_ICASE); -static const SimpleRegexp mediatomb_rx(".*mediatomb.*", REG_ICASE); -static const SimpleRegexp minidlna_rx(".*minidlna.*", REG_ICASE); -static const SimpleRegexp minim_rx(".*minim.*", REG_ICASE); -static const SimpleRegexp twonky_rx(".*twonky.*", REG_ICASE); +static const SimpleRegexp bubble_rx("bubble", REG_ICASE|REG_NOSUB); +static const SimpleRegexp mediatomb_rx("mediatomb", REG_ICASE|REG_NOSUB); +static const SimpleRegexp minidlna_rx("minidlna", REG_ICASE|REG_NOSUB); +static const SimpleRegexp minim_rx("minim", REG_ICASE|REG_NOSUB); +static const SimpleRegexp twonky_rx("twonky", REG_ICASE|REG_NOSUB); ContentDirectory::ContentDirectory(const UPnPDeviceDesc& device, const UPnPServiceDesc& service) @@ -93,17 +96,22 @@ if (bubble_rx(m_modelName)) { m_serviceKind = CDSKIND_BUBBLE; + LOGDEB1("ContentDirectory::ContentDirectory: BUBBLE" << endl); } else if (mediatomb_rx(m_modelName)) { // Readdir by 200 entries is good for most, but MediaTomb likes // them really big. Actually 1000 is better but I don't dare m_rdreqcnt = 500; m_serviceKind = CDSKIND_MEDIATOMB; + LOGDEB1("ContentDirectory::ContentDirectory: MEDIATOMB" << endl); } else if (minidlna_rx(m_modelName)) { m_serviceKind = CDSKIND_MINIDLNA; + LOGDEB1("ContentDirectory::ContentDirectory: MINIDLNA" << endl); } else if (minim_rx(m_modelName)) { m_serviceKind = CDSKIND_MINIM; + LOGDEB1("ContentDirectory::ContentDirectory: MINIM" << endl); } else if (twonky_rx(m_modelName)) { - m_serviceKind = CDSKIND_MINIM; + m_serviceKind = CDSKIND_TWONKY; + LOGDEB1("ContentDirectory::ContentDirectory: TWONKY" << endl); } registerCallback(); }
src/control/#cdirectory.hxx# to libupnpp/control/cdirectory.hxx
--- a/src/control/#cdirectory.hxx# +++ b/libupnpp/control/cdirectory.hxx @@ -61,7 +61,7 @@ ContentDirectory() : m_rdreqcnt(200), m_serviceKind(CDSKIND_UNKNOWN) {} enum ServiceKind {CDSKIND_UNKNOWN, CDSKIND_BUBBLE, CDSKIND_MEDIATOMB, - CDSKIND_MINIDLNA, CDSKIND_MINIM, CDSKIND_TWO}; + CDSKIND_MINIDLNA, CDSKIND_MINIM, CDSKIND_TWONKY}; ServiceKind getKind() {return m_serviceKind;}
src/control/#discovery.cxx# to libupnpp/control/discovery.cxx
--- a/src/control/#discovery.cxx# +++ b/libupnpp/control/discovery.cxx @@ -131,7 +131,7 @@ PTMutexLocker lock(o_downloading_mutex); auto res = o_downloading.insert(tp->url); if (!res.second) { - LOGDEB("discoExplorer: already downloading " << + LOGDEB("discovery:cllb: already downloading " << tp->url << endl); return UPNP_E_SUCCESS; } @@ -140,7 +140,7 @@ LOGDEB("discoExplorer: downloading " << tp->url << endl); string sdesc; if (!downloadUrlWithCurl(tp->url, tp->description, 5)) { - LOGERR("discoExplorer: downloadUrlWithCurl error for: " << + LOGERR("discovery:cllb: downloadUrlWithCurl error for: " << tp->url << endl); {PTMutexLocker lock(o_downloading_mutex); o_downloading.erase(tp->url); @@ -148,7 +148,7 @@ delete tp; return UPNP_E_SUCCESS; } - LOGDEB1("discoExplorer: downloaded description document of " << + LOGDEB1("discovery:cllb: downloaded description document of " << tp->description.size() << " bytes" << endl); {PTMutexLocker lock(o_downloading_mutex); @@ -266,8 +266,8 @@ << " devtype " << d.device.deviceType << endl); { PTMutexLocker lock(o_pool.m_mutex); - LOGINFO("discoExplorer: inserting device id "<< tsk->deviceId - << " description: " << endl << d.device.dump() << endl); + //LOGDEB1("discoExplorer: inserting device id "<< tsk->deviceId + // << " description: " << endl << d.device.dump() << endl); o_pool.m_devices[tsk->deviceId] = d; } {
src/control/#mediaserver.cxx# to libupnpp/control/mediaserver.cxx
--- a/src/control/#mediaserver.cxx# +++ b/libupnpp/control/mediaserver.cxx @@ -75,7 +75,6 @@ } MediaServer::MediaServer(const UPnPDeviceDesc& desc) - : m_serverKind(MSKIND_UNKNOWN) { bool found = false; for (auto it = desc.services.begin(); it != desc.services.end(); it++) { @@ -89,7 +88,6 @@ LOGERR("MediaServer::MediaServer: ContentDirectory service not " "found in device" << endl); } - if (str } }