From 61df54155a3cb1846e6bf15e4f007ec8d623de63 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dockes Date: Sun, 23 Aug 2020 14:22:21 +0200 Subject: [PATCH] Modification to use npupnp instead of pupnp when the upnp meson option is set --- meson_options.txt | 5 +- .../plugins/upnp/ContentDirectoryService.cxx | 101 ++++++++++++++++++ src/lib/upnp/Action.hxx | 2 + src/lib/upnp/ClientInit.cxx | 12 +-- src/lib/upnp/Compat.hxx | 4 +- src/lib/upnp/ContentDirectoryService.cxx | 25 +++++ src/lib/upnp/Init.cxx | 4 + src/lib/upnp/UniqueIxml.hxx | 2 + src/lib/upnp/ixmlwrap.cxx | 4 + src/lib/upnp/ixmlwrap.hxx | 2 + src/lib/upnp/meson.build | 20 +++- 11 files changed, 170 insertions(+), 11 deletions(-) --- a/meson_options.txt +++ b/meson_options.txt @@ -54,7 +54,10 @@ option('dsd', type: 'boolean', value: tr # option('database', type: 'boolean', value: true, description: 'enable support for the music database') -option('upnp', type: 'feature', description: 'UPnP client support') +option('upnp', type: 'combo', + choices: ['auto', 'pupnp', 'npupnp', 'disabled'], + value: 'auto', + description: 'UPnP client support') option('libmpdclient', type: 'feature', description: 'libmpdclient support (for the proxy database plugin)') # --- a/src/db/plugins/upnp/ContentDirectoryService.cxx +++ b/src/db/plugins/upnp/ContentDirectoryService.cxx @@ -18,7 +18,10 @@ */ #include "lib/upnp/ContentDirectoryService.hxx" +#include "config.h" +#ifdef USING_PUPNP #include "lib/upnp/ixmlwrap.hxx" +#endif #include "lib/upnp/UniqueIxml.hxx" #include "lib/upnp/Action.hxx" #include "Directory.hxx" @@ -28,8 +31,11 @@ #include "util/ScopeExit.hxx" #include "util/StringFormat.hxx" +#include + #include +#ifdef USING_PUPNP static void ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response) { @@ -39,6 +45,7 @@ ReadResultTag(UPnPDirContent &dirbuf, IX dirbuf.Parse(p); } +#endif inline void ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl, @@ -47,6 +54,7 @@ ContentDirectoryService::readDirSlice(Up unsigned &didreadp, unsigned &totalp) const { +#ifdef USING_PUPNP // Some devices require an empty SortCriteria, else bad params IXML_Document *request = MakeActionHelper("Browse", m_serviceType.c_str(), @@ -82,6 +90,37 @@ ContentDirectoryService::readDirSlice(Up totalp = ParseUnsigned(value); ReadResultTag(dirbuf, response); +#else + std::vector > actionParams{ + { "ObjectID", objectId }, + { "BrowseFlag", "BrowseDirectChildren" }, + { "Filter", "*" }, + { "SortCriteria", "" }, + { "StartingIndex", StringFormat<32>("%u", offset).c_str() }, + { "RequestedCount", StringFormat<32>("%u", count).c_str() } + }; + std::vector > responseData; + int errcode; + std::string errdesc; + int code = + UpnpSendAction(hdl, "", m_actionURL, m_serviceType, "Browse", + actionParams, responseData, &errcode, errdesc); + if (code != UPNP_E_SUCCESS) + throw FormatRuntimeError("UpnpSendAction() failed: %s", + UpnpGetErrorMessage(code)); + const char *p = ""; + didreadp = 0; + for (const auto &entry : responseData) { + if (entry.first == "Result") { + p = entry.second.c_str(); + } else if (entry.first == "TotalMatches") { + totalp = ParseUnsigned(entry.second.c_str()); + } else if (entry.first == "NumberReturned") { + didreadp = ParseUnsigned(entry.second.c_str()); + } + } + dirbuf.Parse(p); +#endif } UPnPDirContent @@ -110,6 +149,7 @@ ContentDirectoryService::search(UpnpClie unsigned offset = 0, total = -1, count; do { +#ifdef USING_PUPNP UniqueIxmlDocument request(MakeActionHelper("Search", m_serviceType.c_str(), "ContainerID", objectId, "SearchCriteria", ss, @@ -147,6 +187,39 @@ ContentDirectoryService::search(UpnpClie total = ParseUnsigned(value); ReadResultTag(dirbuf, response.get()); +#else + std::vector > actionParams{ + { "ContainerID", objectId }, + { "SearchCriteria", ss }, + { "Filter", "*" }, + { "SortCriteria", "" }, + { "StartingIndex", + StringFormat<32>("%u", offset).c_str() }, + { "RequestedCount", "0" } + }; + std::vector > responseData; + int errcode; + std::string errdesc; + int code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType, + "Search", actionParams, responseData, + &errcode, errdesc); + if (code != UPNP_E_SUCCESS) + throw FormatRuntimeError("UpnpSendAction() failed: %s", + UpnpGetErrorMessage(code)); + const char *p = ""; + count = 0; + for (const auto &entry : responseData) { + if (entry.first == "Result") { + p = entry.second.c_str(); + } else if (entry.first == "TotalMatches") { + total = ParseUnsigned(entry.second.c_str()); + } else if (entry.first == "NumberReturned") { + count = ParseUnsigned(entry.second.c_str()); + offset += count; + } + } + dirbuf.Parse(p); +#endif } while (count > 0 && offset < total); return dirbuf; @@ -156,6 +229,7 @@ UPnPDirContent ContentDirectoryService::getMetadata(UpnpClient_Handle hdl, const char *objectId) const { +#ifdef USING_PUPNP // Create request UniqueIxmlDocument request(MakeActionHelper("Browse", m_serviceType.c_str(), "ObjectID", objectId, @@ -179,4 +253,31 @@ ContentDirectoryService::getMetadata(Upn UPnPDirContent dirbuf; ReadResultTag(dirbuf, response.get()); return dirbuf; +#else + std::vector > actionParams{ + { "ObjectID", objectId }, { "BrowseFlag", "BrowseMetadata" }, + { "Filter", "*" }, { "SortCriteria", "" }, + { "StartingIndex", "0" }, { "RequestedCount", "1" } + }; + std::vector > responseData; + int errcode; + std::string errdesc; + int code = + UpnpSendAction(hdl, "", m_actionURL, m_serviceType, "Browse", + actionParams, responseData, &errcode, errdesc); + if (code != UPNP_E_SUCCESS) + throw FormatRuntimeError("UpnpSendAction() failed: %s", + UpnpGetErrorMessage(code)); + const char *p = ""; + for (const auto &entry : responseData) { + if (entry.first == "Result") { + p = entry.second.c_str(); + break; + } + } + + UPnPDirContent dirbuf; + dirbuf.Parse(p); + return dirbuf; +#endif } --- a/src/lib/upnp/Action.hxx +++ b/src/lib/upnp/Action.hxx @@ -38,6 +38,7 @@ CountNameValuePairs(gcc_unused const cha return 1 + CountNameValuePairs(args...); } +#ifdef USING_PUPNP /** * A wrapper for UpnpMakeAction() that counts the number of name/value * pairs and adds the nullptr sentinel. @@ -52,5 +53,6 @@ MakeActionHelper(const char *action_name args..., nullptr, nullptr); } +#endif #endif --- a/src/lib/upnp/ClientInit.cxx +++ b/src/lib/upnp/ClientInit.cxx @@ -31,14 +31,12 @@ static Mutex upnp_client_init_mutex; static unsigned upnp_client_ref; static UpnpClient_Handle upnp_client_handle; -static int -UpnpClientCallback(Upnp_EventType et, -#if UPNP_VERSION >= 10800 - const +static int UpnpClientCallback(Upnp_EventType et, +#if 1 + const #endif - void *evp, - void *cookie) noexcept -{ + void *evp, + void *cookie) noexcept { if (cookie == nullptr) /* this is the cookie passed to UpnpRegisterClient(); but can this ever happen? Will libupnp ever invoke --- a/src/lib/upnp/Compat.hxx +++ b/src/lib/upnp/Compat.hxx @@ -22,14 +22,14 @@ #include -#if UPNP_VERSION < 10800 +#if 0 /* emulate the libupnp 1.8 API with older versions */ using UpnpDiscovery = Upnp_Discovery; #endif -#if UPNP_VERSION < 10624 +#if 0 #include "util/Compiler.h" gcc_pure --- a/src/lib/upnp/ContentDirectoryService.cxx +++ b/src/lib/upnp/ContentDirectoryService.cxx @@ -17,15 +17,21 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "config.h" + #include "ContentDirectoryService.hxx" #include "UniqueIxml.hxx" #include "Device.hxx" +#ifdef USING_PUPNP #include "ixmlwrap.hxx" +#endif #include "Action.hxx" #include "util/UriUtil.hxx" #include "util/RuntimeError.hxx" #include "util/SplitString.hxx" +#include + ContentDirectoryService::ContentDirectoryService(const UPnPDevice &device, const UPnPService &service) noexcept :m_actionURL(uri_apply_base(service.controlURL, device.URLBase)), @@ -51,6 +57,7 @@ ContentDirectoryService::~ContentDirecto std::forward_list ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const { +#ifdef USING_PUPNP UniqueIxmlDocument request(UpnpMakeAction("GetSearchCapabilities", m_serviceType.c_str(), 0, nullptr, nullptr)); @@ -69,6 +76,24 @@ ContentDirectoryService::getSearchCapabi const char *s = ixmlwrap::getFirstElementValue(response.get(), "SearchCaps"); +#else + std::vector > responseData; + int errcode; + std::string errdesc; + auto code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType, + "GetSearchCapabilities", {}, responseData, + &errcode, errdesc); + if (code != UPNP_E_SUCCESS) + throw FormatRuntimeError("UpnpSendAction() failed: %s", + UpnpGetErrorMessage(code)); + const char *s{ nullptr }; + for (auto &entry : responseData) { + if (entry.first == "SearchCaps") { + s = entry.second.c_str(); + break; + } + } +#endif if (s == nullptr || *s == 0) /* we could just "return {}" here, but GCC 5 doesn't understand that */ --- a/src/lib/upnp/Init.cxx +++ b/src/lib/upnp/Init.cxx @@ -23,7 +23,9 @@ #include #include +#ifdef USING_PUPNP #include +#endif #include @@ -44,8 +46,10 @@ DoInit() UpnpSetMaxContentLength(2000*1024); +#ifdef USING_PUPNP // Servers sometimes make error (e.g.: minidlna returns bad utf-8) ixmlRelaxParser(1); +#endif } void --- a/src/lib/upnp/UniqueIxml.hxx +++ b/src/lib/upnp/UniqueIxml.hxx @@ -20,6 +20,7 @@ #ifndef MPD_UPNP_UNIQUE_XML_HXX #define MPD_UPNP_UNIQUE_XML_HXX +#ifdef USING_PUPNP #include #include @@ -37,4 +38,5 @@ struct UpnpIxmlDeleter { typedef std::unique_ptr UniqueIxmlDocument; typedef std::unique_ptr UniqueIxmlNodeList; +#endif /* USING_PUPNP */ #endif --- a/src/lib/upnp/ixmlwrap.cxx +++ b/src/lib/upnp/ixmlwrap.cxx @@ -15,6 +15,9 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "config.h" + +#ifdef USING_PUPNP #include "ixmlwrap.hxx" #include "UniqueIxml.hxx" @@ -39,3 +42,4 @@ getFirstElementValue(IXML_Document *doc, } } +#endif --- a/src/lib/upnp/ixmlwrap.hxx +++ b/src/lib/upnp/ixmlwrap.hxx @@ -17,6 +17,7 @@ #ifndef _IXMLWRAP_H_INCLUDED_ #define _IXMLWRAP_H_INCLUDED_ +#ifdef USING_PUPNP #include #include @@ -32,4 +33,5 @@ namespace ixmlwrap { } +#endif /* USING_PUPNP */ #endif /* _IXMLWRAP_H_INCLUDED_ */ --- a/src/lib/upnp/meson.build +++ b/src/lib/upnp/meson.build @@ -1,4 +1,22 @@ -upnp_dep = dependency('libupnp', required: get_option('upnp')) +upnp_option = get_option('upnp') + +if upnp_option == 'auto' + upnp_dep = dependency('libupnp', version: '>= 1.8', required: false) + conf.set('USING_PUPNP', upnp_dep.found()) + if not upnp_dep.found() + upnp_dep = dependency('libnpupnp', version: '>= 1.8', required: false) + endif +elif upnp_option == 'pupnp' + upnp_dep = dependency('libupnp', version: '>= 1.8', required: true) + conf.set('USING_PUPNP', true) +elif upnp_option == 'npupnp' + upnp_dep = dependency('libnpupnp', required: true) + conf.set('USING_PUPNP', false) +elif upnp_option == 'disabled' + upnp_dep = dependency('', required: false) + subdir_done() +endif + conf.set('ENABLE_UPNP', upnp_dep.found()) if not upnp_dep.found() subdir_done()