Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libupnpp for openSUSE:Factory checked in at 2021-04-15 16:57:56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libupnpp (Old) and /work/SRC/openSUSE:Factory/.libupnpp.new.12324 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libupnpp" Thu Apr 15 16:57:56 2021 rev:3 rq:885577 version:0.21.0 Changes: -------- --- /work/SRC/openSUSE:Factory/libupnpp/libupnpp.changes 2021-01-20 18:28:50.859596005 +0100 +++ /work/SRC/openSUSE:Factory/.libupnpp.new.12324/libupnpp.changes 2021-04-15 16:58:40.330793434 +0200 @@ -1,0 +2,11 @@ +Thu Apr 15 00:53:18 UTC 2021 - Ferdinand Thiessen <[email protected]> + +- Update to version 0.21.0: + * Allow configuring the subscription timeout (init option) + * Add interface for the lib to report a subscription autorenewal + failure, and to renew all subscriptions. + * Add API to set the product/version values in User-Agent + and Server headers. +- Modified description + +------------------------------------------------------------------- @@ -5 +16,2 @@ - * No upstream changelog available + * Content Directory interface: accept responses with 0 TotalMatches. + * Adjust excessively noisy messages. Old: ---- libupnpp-0.20.2.tar.gz New: ---- libupnpp-0.21.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libupnpp.spec ++++++ --- /var/tmp/diff_new_pack.y8jIJb/_old 2021-04-15 16:58:40.730794067 +0200 +++ /var/tmp/diff_new_pack.y8jIJb/_new 2021-04-15 16:58:40.730794067 +0200 @@ -16,14 +16,14 @@ # -%define so_ver 9 +%define so_ver 11 Name: libupnpp -Version: 0.20.2 +Version: 0.21.0 Release: 0 -Summary: Library providing a higher level C++ API over libnpupnp or libupnp +Summary: Library providing a higher level API over libnpupnp or libupnp License: GPL-2.0-or-later Group: Development/Libraries/C and C++ -URL: https://www.lesbonscomptes.com/updmpdcli +URL: https://www.lesbonscomptes.com/upmpdcli/index.html Source0: https://www.lesbonscomptes.com/upmpdcli/downloads/libupnpp-%{version}.tar.gz BuildRequires: gcc-c++ BuildRequires: pkgconfig @@ -32,14 +32,24 @@ BuildRequires: pkgconfig(libnpupnp) %description -Library providing a higher level C++ API over libnpupnp or libupnp +Libupnpp is a C++ wrapper for libupnp a.k.a Portable UPnP (up to branch 0.17), +or its C++ descendant, libnpupnp (versions 0.18 and later). + +Libupnpp can be used to implement UPnP devices and services, or Control Points. +The Control Point side of libupnpp, which is documented here, +allows a C++ program to discover UPnP devices, and exchange commands and status with them. %package -n %{name}%{so_ver} Summary: Library providing a higher level C++ API over libnpupnp or libupnp Group: System/Libraries %description -n %{name}%{so_ver} -Library providing a higher level C++ API over libnpupnp or libupnp +Libupnpp is a C++ wrapper for libupnp a.k.a Portable UPnP (up to branch 0.17), +or its C++ descendant, libnpupnp (versions 0.18 and later). + +Libupnpp can be used to implement UPnP devices and services, or Control Points. +The Control Point side of libupnpp, which is documented here, +allows a C++ program to discover UPnP devices, and exchange commands and status with them. %package devel Summary: Development files for %{name} @@ -68,7 +78,7 @@ %license COPYING %{_libdir}/*.so.* -%files -n libupnpp-devel +%files devel %{_includedir}/* %{_libdir}/*.so %{_libdir}/pkgconfig/*.pc ++++++ libupnpp-0.20.2.tar.gz -> libupnpp-0.21.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/configure new/libupnpp-0.21.0/configure --- old/libupnpp-0.20.2/configure 2020-12-30 15:06:37.000000000 +0100 +++ new/libupnpp-0.21.0/configure 2021-03-13 13:58:07.000000000 +0100 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for libupnpp 0.20.2. +# Generated by GNU Autoconf 2.69 for libupnpp 0.21.0. # # Report bugs to <[email protected]>. # @@ -590,8 +590,8 @@ # Identity of this package. PACKAGE_NAME='libupnpp' PACKAGE_TARNAME='libupnpp' -PACKAGE_VERSION='0.20.2' -PACKAGE_STRING='libupnpp 0.20.2' +PACKAGE_VERSION='0.21.0' +PACKAGE_STRING='libupnpp 0.21.0' PACKAGE_BUGREPORT='[email protected]' PACKAGE_URL='http://www.lesbonscomptes.com/upmpdcli' @@ -1362,7 +1362,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libupnpp 0.20.2 to adapt to many kinds of systems. +\`configure' configures libupnpp 0.21.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1433,7 +1433,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libupnpp 0.20.2:";; + short | recursive ) echo "Configuration of libupnpp 0.21.0:";; esac cat <<\_ACEOF @@ -1559,7 +1559,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libupnpp configure 0.20.2 +libupnpp configure 0.21.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2012,7 +2012,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libupnpp $as_me 0.20.2, which was +It was created by libupnpp $as_me 0.21.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2374,11 +2374,9 @@ # - If any interfaces have been added since the last public release, then # increment age. # - If any interfaces have been removed or changed since the last public -# release, then set age to 0 AND CHANGE PACKAGE NAME. -# libupnpp packages are named libupnppX where X is the .so major number -# (c-a). This allows packages for multiple incompatible ABIs to be -# installed -VERSION_INFO=10:0:1 +# release, then set age to 0 and change the package name if multiple +# versions need to be co-installed. +VERSION_INFO=11:0:0 @@ -3006,7 +3004,7 @@ # Define the identity of the package. PACKAGE='libupnpp' - VERSION='0.20.2' + VERSION='0.21.0' # Some tools Automake needs. @@ -16930,7 +16928,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libupnpp $as_me 0.20.2, which was +This file was extended by libupnpp $as_me 0.21.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -16997,7 +16995,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -libupnpp config.status 0.20.2 +libupnpp config.status 0.21.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/configure.ac new/libupnpp-0.21.0/configure.ac --- old/libupnpp-0.20.2/configure.ac 2020-12-30 15:05:49.000000000 +0100 +++ new/libupnpp-0.21.0/configure.ac 2021-01-24 18:20:31.000000000 +0100 @@ -4,7 +4,7 @@ # occur with revision (3rd number) changes. # !!!! When changing the version, also change the defines in upnpplib.hxx !!! -AC_INIT([libupnpp], [0.20.2], [[email protected]], +AC_INIT([libupnpp], [0.21.0], [[email protected]], [libupnpp], [http://www.lesbonscomptes.com/upmpdcli]) # Lib version info. See: @@ -20,11 +20,9 @@ # - If any interfaces have been added since the last public release, then # increment age. # - If any interfaces have been removed or changed since the last public -# release, then set age to 0 AND CHANGE PACKAGE NAME. -# libupnpp packages are named libupnppX where X is the .so major number -# (c-a). This allows packages for multiple incompatible ABIs to be -# installed -VERSION_INFO=10:0:1 +# release, then set age to 0 and change the package name if multiple +# versions need to be co-installed. +VERSION_INFO=11:0:0 AC_PREREQ([2.53]) AC_CONFIG_SRCDIR([libupnpp/upnpplib.hxx]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/base64.cxx new/libupnpp-0.21.0/libupnpp/base64.cxx --- old/libupnpp-0.20.2/libupnpp/base64.cxx 2020-07-01 17:18:31.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/base64.cxx 2021-01-01 16:46:04.000000000 +0100 @@ -219,7 +219,8 @@ unsigned char output[4]; out.clear(); - + out.reserve(((in.size()) * 4)/3 + 4); + int srclength = in.length(); int sidx = 0; while (2 < srclength) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/control/mediarenderer.cxx new/libupnpp-0.21.0/libupnpp/control/mediarenderer.cxx --- old/libupnpp-0.20.2/libupnpp/control/mediarenderer.cxx 2020-06-23 12:43:30.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/control/mediarenderer.cxx 2021-02-10 12:02:25.000000000 +0100 @@ -75,7 +75,7 @@ (RenderingControl::isRDCService(service.serviceType) || OHProduct::isOHPrService(service.serviceType)) && - (friendlyName.empty() || !friendlyName.compare(device.friendlyName))) { + (friendlyName.empty() || friendlyName == device.friendlyName)) { //LOGDEB("MDAccum setting " << device.UDN << endl); (*out)[device.UDN] = device; } @@ -87,12 +87,12 @@ { std::unordered_map<string, UPnPDeviceDesc> mydevs; - UPnPDeviceDirectory::Visitor visitor = bind(MDAccum, &mydevs, friendlyName, - _1, _2); + UPnPDeviceDirectory::Visitor visitor = bind( + MDAccum, &mydevs, friendlyName, _1, _2); UPnPDeviceDirectory::getTheDir()->traverse(visitor); - for (std::unordered_map<string, UPnPDeviceDesc>::iterator it = - mydevs.begin(); it != mydevs.end(); it++) - devices.push_back(it->second); + for (const auto& dev : mydevs) { + devices.push_back(dev.second); + } return !devices.empty(); } @@ -115,6 +115,35 @@ return ohpr() ? true : false; } + +#define RESUBS_ONE(S) \ + do { \ + auto ptr = (S).lock(); \ + if (ptr) { \ + ok = ptr->reSubscribe(); \ + if (!ok) { \ + return false; \ + } \ + } \ + } while (false); + +bool MediaRenderer::reSubscribeAll() +{ + bool ok; + RESUBS_ONE(m->rdc); + RESUBS_ONE(m->avt); + RESUBS_ONE(m->cnm); + RESUBS_ONE(m->ohpr); + RESUBS_ONE(m->ohpl); + RESUBS_ONE(m->ohtm); + RESUBS_ONE(m->ohvl); + RESUBS_ONE(m->ohrc); + RESUBS_ONE(m->ohrd); + RESUBS_ONE(m->ohif); + RESUBS_ONE(m->ohsn); + return true; +} + RDCH MediaRenderer::rdc() { if (desc() == 0) @@ -123,10 +152,9 @@ RDCH rdcl = m->rdc.lock(); if (rdcl) return rdcl; - for (vector<UPnPServiceDesc>::const_iterator it = desc()->services.begin(); - it != desc()->services.end(); it++) { - if (RenderingControl::isRDCService(it->serviceType)) { - rdcl = RDCH(new RenderingControl(*desc(), *it)); + for (const auto& service : desc()->services) { + if (RenderingControl::isRDCService(service.serviceType)) { + rdcl = RDCH(new RenderingControl(*desc(), service)); break; } } @@ -141,10 +169,9 @@ AVTH avtl = m->avt.lock(); if (avtl) return avtl; - for (vector<UPnPServiceDesc>::const_iterator it = desc()->services.begin(); - it != desc()->services.end(); it++) { - if (AVTransport::isAVTService(it->serviceType)) { - avtl = AVTH(new AVTransport(*desc(), *it)); + for (const auto& service : desc()->services) { + if (AVTransport::isAVTService(service.serviceType)) { + avtl = AVTH(new AVTransport(*desc(), service)); break; } } @@ -177,10 +204,9 @@ OHPRH ohprl = m->ohpr.lock(); if (ohprl) return ohprl; - for (vector<UPnPServiceDesc>::const_iterator it = desc()->services.begin(); - it != desc()->services.end(); it++) { - if (OHProduct::isOHPrService(it->serviceType)) { - ohprl = OHPRH(new OHProduct(*desc(), *it)); + for (const auto& service : desc()->services) { + if (OHProduct::isOHPrService(service.serviceType)) { + ohprl = OHPRH(new OHProduct(*desc(), service)); break; } } @@ -195,10 +221,9 @@ OHPLH ohpll = m->ohpl.lock(); if (ohpll) return ohpll; - for (vector<UPnPServiceDesc>::const_iterator it = desc()->services.begin(); - it != desc()->services.end(); it++) { - if (OHPlaylist::isOHPlService(it->serviceType)) { - ohpll = OHPLH(new OHPlaylist(*desc(), *it)); + for (const auto& service : desc()->services) { + if (OHPlaylist::isOHPlService(service.serviceType)) { + ohpll = OHPLH(new OHPlaylist(*desc(), service)); break; } } @@ -213,10 +238,9 @@ OHRCH ohrcl = m->ohrc.lock(); if (ohrcl) return ohrcl; - for (vector<UPnPServiceDesc>::const_iterator it = desc()->services.begin(); - it != desc()->services.end(); it++) { - if (OHReceiver::isOHRcService(it->serviceType)) { - ohrcl = OHRCH(new OHReceiver(*desc(), *it)); + for (const auto& service : desc()->services) { + if (OHReceiver::isOHRcService(service.serviceType)) { + ohrcl = OHRCH(new OHReceiver(*desc(), service)); break; } } @@ -231,10 +255,9 @@ OHRDH handle = m->ohrd.lock(); if (handle) return handle; - for (vector<UPnPServiceDesc>::const_iterator it = desc()->services.begin(); - it != desc()->services.end(); it++) { - if (OHRadio::isOHRdService(it->serviceType)) { - handle = OHRDH(new OHRadio(*desc(), *it)); + for (const auto& service : desc()->services) { + if (OHRadio::isOHRdService(service.serviceType)) { + handle = OHRDH(new OHRadio(*desc(), service)); break; } } @@ -249,10 +272,9 @@ OHIFH handle = m->ohif.lock(); if (handle) return handle; - for (vector<UPnPServiceDesc>::const_iterator it = desc()->services.begin(); - it != desc()->services.end(); it++) { - if (OHInfo::isOHInfoService(it->serviceType)) { - handle = OHIFH(new OHInfo(*desc(), *it)); + for (const auto& service : desc()->services) { + if (OHInfo::isOHInfoService(service.serviceType)) { + handle = OHIFH(new OHInfo(*desc(), service)); break; } } @@ -267,10 +289,9 @@ OHSNH handle = m->ohsn.lock(); if (handle) return handle; - for (vector<UPnPServiceDesc>::const_iterator it = desc()->services.begin(); - it != desc()->services.end(); it++) { - if (OHSender::isOHSenderService(it->serviceType)) { - handle = OHSNH(new OHSender(*desc(), *it)); + for (const auto& service : desc()->services) { + if (OHSender::isOHSenderService(service.serviceType)) { + handle = OHSNH(new OHSender(*desc(), service)); break; } } @@ -285,10 +306,9 @@ OHTMH ohtml = m->ohtm.lock(); if (ohtml) return ohtml; - for (vector<UPnPServiceDesc>::const_iterator it = desc()->services.begin(); - it != desc()->services.end(); it++) { - if (OHTime::isOHTMService(it->serviceType)) { - ohtml = OHTMH(new OHTime(*desc(), *it)); + for (const auto& service : desc()->services) { + if (OHTime::isOHTMService(service.serviceType)) { + ohtml = OHTMH(new OHTime(*desc(), service)); break; } } @@ -303,10 +323,9 @@ OHVLH ohvll = m->ohvl.lock(); if (ohvll) return ohvll; - for (vector<UPnPServiceDesc>::const_iterator it = desc()->services.begin(); - it != desc()->services.end(); it++) { - if (OHVolume::isOHVLService(it->serviceType)) { - ohvll = OHVLH(new OHVolume(*desc(), *it)); + for (const auto& service : desc()->services) { + if (OHVolume::isOHVLService(service.serviceType)) { + ohvll = OHVLH(new OHVolume(*desc(), service)); break; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/control/mediarenderer.hxx new/libupnpp-0.21.0/libupnpp/control/mediarenderer.hxx --- old/libupnpp-0.20.2/libupnpp/control/mediarenderer.hxx 2020-09-21 09:28:17.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/control/mediarenderer.hxx 2021-01-02 10:40:11.000000000 +0100 @@ -78,6 +78,8 @@ const std::string& friendlyName = ""); static bool isMRDevice(const std::string& devicetype); + bool reSubscribeAll(); + protected: static const std::string DType; private: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/control/ohinfo.cxx new/libupnpp-0.21.0/libupnpp/control/ohinfo.cxx --- old/libupnpp-0.20.2/libupnpp/control/ohinfo.cxx 2020-11-26 09:26:06.000000000 +0100 +++ new/libupnpp-0.21.0/libupnpp/control/ohinfo.cxx 2021-01-12 10:02:23.000000000 +0100 @@ -92,7 +92,7 @@ } string didl; if (!data.get("Value", &didl)) { - LOGERR("OHInfo::Read: missing Value in response" << endl); + LOGERR("OHInfo::metatext: missing Value in response" << endl); return UPNP_E_BAD_RESPONSE; } if (didl.empty()) { @@ -102,4 +102,119 @@ return OHRadio::decodeMetadata("OHInfo::metatext", didl, dirent); } +int OHInfo::track(std::string *uri, UPnPDirObject *dirent) +{ + SoapOutgoing args(getServiceType(), "Counters"); + SoapIncoming data; + int ret = runAction(args, data); + if (ret != UPNP_E_SUCCESS) { + LOGERR("OHInfo::counters: runAction failed\n"); + return ret; + } + if (uri) { + if (!data.get("Uri", uri)) { + LOGERR("OHInfo::track: missing Uri in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + } + if (dirent) { + string didl; + if (!data.get("Metadata", &didl)) { + LOGERR("OHInfo::track: missing Metadata in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + return OHRadio::decodeMetadata("OHInfo::metatext", didl, dirent); + } + return UPNP_E_SUCCESS; +} + +int OHInfo::counters(int *trackcount, int *detailscount, int *metatextcount) +{ + SoapOutgoing args(getServiceType(), "Counters"); + SoapIncoming data; + int ret = runAction(args, data); + if (ret != UPNP_E_SUCCESS) { + LOGERR("OHInfo::counters: runAction failed\n"); + return ret; + } + if (trackcount) { + const char *nm = "TrackCount"; + if (!data.get(nm, trackcount)) { + LOGERR("OHInfo::counters: missing " << nm << " in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + } + if (detailscount) { + const char *nm = "DetailsCount"; + if (!data.get(nm, detailscount)) { + LOGERR("OHInfo::counters: missing " << nm << " in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + } + if (metatextcount) { + const char *nm = "MetatextCount"; + if (!data.get(nm, metatextcount)) { + LOGERR("OHInfo::counters: missing " << nm << " in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + } + return UPNP_E_SUCCESS; +} + + +int OHInfo::details(int *duration, int *bitrate, int *bitdepth, int *samplerate, bool *lossless, + std::string *codecname) +{ + SoapOutgoing args(getServiceType(), "Details"); + SoapIncoming data; + int ret = runAction(args, data); + if (ret != UPNP_E_SUCCESS) { + LOGERR("OHInfo::details: runAction failed\n"); + return ret; + } + if (duration) { + const char *nm = "Duration"; + if (!data.get(nm, duration)) { + LOGERR("OHInfo::counters: missing " << nm << " in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + } + if (bitrate) { + const char *nm = "BitRate"; + if (!data.get(nm, bitrate)) { + LOGERR("OHInfo::counters: missing " << nm << " in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + } + if (bitdepth) { + const char *nm = "BitDepth"; + if (!data.get(nm, bitdepth)) { + LOGERR("OHInfo::counters: missing " << nm << " in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + } + if (samplerate) { + const char *nm = "SampleRate"; + if (!data.get(nm, samplerate)) { + LOGERR("OHInfo::counters: missing " << nm << " in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + } + if (lossless) { + const char *nm = "Lossless"; + if (!data.get(nm, lossless)) { + LOGERR("OHInfo::counters: missing " << nm << " in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + } + if (codecname) { + const char *nm = "Codecname"; + if (!data.get(nm, codecname)) { + LOGERR("OHInfo::counters: missing " << nm << " in response" << endl); + return UPNP_E_BAD_RESPONSE; + } + } + return UPNP_E_SUCCESS; +} + } // End namespace UPnPClient diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/control/ohinfo.hxx new/libupnpp-0.21.0/libupnpp/control/ohinfo.hxx --- old/libupnpp-0.20.2/libupnpp/control/ohinfo.hxx 2020-09-21 09:29:22.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/control/ohinfo.hxx 2021-01-13 08:57:55.000000000 +0100 @@ -51,6 +51,10 @@ static bool isOHInfoService(const std::string& st); virtual bool serviceTypeMatch(const std::string& tp); + int counters(int *trackcount, int *detailscount, int *metatextcount); + int track(std::string *uri, UPnPDirObject *dirent); + int details(int *duration, int *bitrate, int *bitdepth, int *samplerate, + bool *lossless, std::string *codecname); int metatext(UPnPDirObject *dirent); protected: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/control/ohplaylist.cxx new/libupnpp-0.21.0/libupnpp/control/ohplaylist.cxx --- old/libupnpp-0.20.2/libupnpp/control/ohplaylist.cxx 2020-07-28 13:25:22.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/control/ohplaylist.cxx 2021-01-03 14:26:37.000000000 +0100 @@ -80,44 +80,48 @@ const std::unordered_map<std::string, std::string>& props) { LOGDEB1("OHPlaylist::evtCallback: getReporter(): " << getReporter() << endl); - for (std::unordered_map<std::string, std::string>::const_iterator it = - props.begin(); it != props.end(); it++) { - if (!getReporter()) { - LOGDEB1("OHPlaylist::evtCallback: " << it->first << " -> " - << it->second << endl); + auto reporter = getReporter(); + if (reporter && props.empty()) { + // Subscription renewal failed + reporter->autorenew_failed(); + return; + } + for (const auto& prop : props) { + if (!reporter) { + // For logging with no reporter set + LOGDEB1("OHPlaylist::evtCallback: " << prop.first << " -> " + << prop.second << endl); continue; } - if (!it->first.compare("TransportState")) { + if (prop.first == "TransportState") { TPState tp; - stringToTpState(it->second, &tp); - getReporter()->changed(it->first.c_str(), int(tp)); + stringToTpState(prop.second, &tp); + getReporter()->changed(prop.first.c_str(), int(tp)); - } else if (!it->first.compare("ProtocolInfo")) { - getReporter()->changed(it->first.c_str(), - it->second.c_str()); + } else if (prop.first == "ProtocolInfo") { + getReporter()->changed(prop.first.c_str(), + prop.second.c_str()); - } else if (!it->first.compare("Repeat") || - !it->first.compare("Shuffle")) { + } else if (prop.first == "Repeat" || prop.first == "Shuffle") { bool val = false; - stringToBool(it->second, &val); - getReporter()->changed(it->first.c_str(), val ? 1 : 0); + stringToBool(prop.second, &val); + getReporter()->changed(prop.first.c_str(), val ? 1 : 0); - } else if (!it->first.compare("Id") || - !it->first.compare("TracksMax")) { - getReporter()->changed(it->first.c_str(), - atoi(it->second.c_str())); + } else if (prop.first == "Id" || prop.first == "TracksMax") { + getReporter()->changed(prop.first.c_str(), + atoi(prop.second.c_str())); - } else if (!it->first.compare("IdArray")) { + } else if (prop.first == "IdArray") { // Decode IdArray. See how we call the client vector<int> v; - ohplIdArrayToVec(it->second, &v); - getReporter()->changed(it->first.c_str(), v); + ohplIdArrayToVec(prop.second, &v); + getReporter()->changed(prop.first.c_str(), v); } else { LOGERR("OHPlaylist event: unknown variable: name [" << - it->first << "] value [" << it->second << endl); - getReporter()->changed(it->first.c_str(), it->second.c_str()); + prop.first << "] value [" << prop.second << endl); + getReporter()->changed(prop.first.c_str(), prop.second.c_str()); } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/control/service.cxx new/libupnpp-0.21.0/libupnpp/control/service.cxx --- old/libupnpp-0.20.2/libupnpp/control/service.cxx 2020-07-28 13:25:22.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/control/service.cxx 2021-01-03 14:21:22.000000000 +0100 @@ -77,10 +77,10 @@ * libupnp to call the appropriate object method when it receives * an event. */ static std::unordered_map<std::string, evtCBFunc> o_calls; +static std::mutex cblock; -Service::Service(const UPnPDeviceDesc& devdesc, - const UPnPServiceDesc& servdesc) +Service::Service(const UPnPDeviceDesc& devdesc, const UPnPServiceDesc& servdesc) { if ((m = new Internal()) == 0) { LOGERR("Device::Device: out of memory" << endl); @@ -225,41 +225,38 @@ return runAction(args, data); } -static std::mutex cblock; // The static event callback given to libupnp static int srvCB(Upnp_EventType et, CBCONST void* vevp, void*) { std::unique_lock<std::mutex> lock(cblock); - LOGDEB0("Service:srvCB: " << evTypeAsString(et) << endl); + // All event types begin with a SID field + const char *sid = UpnpEvent_get_SID_cstr((UpnpEvent*)vevp); + + LOGDEB0("Service:srvCB: " << evTypeAsString(et) << " SID " << sid << endl); + + auto it = o_calls.find(sid); switch (et) { - case UPNP_EVENT_RENEWAL_COMPLETE: - case UPNP_EVENT_SUBSCRIBE_COMPLETE: - case UPNP_EVENT_UNSUBSCRIBE_COMPLETE: case UPNP_EVENT_AUTORENEWAL_FAILED: { - const char *ff = (const char *)vevp; - (void)ff; - LOGDEB1("Service:srvCB: subs event: " << ff << endl); + if (it != o_calls.end()) { + it->second({}); + } break; } - case UPNP_EVENT_RECEIVED: { UpnpEvent *evp = (UpnpEvent *)vevp; - LOGDEB1("Service:srvCB: var change event: SID " << - UpnpEvent_get_SID_cstr(evp) << " EventKey " << - UpnpEvent_get_EventKey(evp) << " changed " << + LOGDEB1("Service:srvCB: var change event: SID " << sid << + " EventKey " << UpnpEvent_get_EventKey(evp) << " changed " << SoapHelp::argsToString(evp->ChangedVariables.begin(), - evp->ChangedVariables.end()) << endl); + evp->ChangedVariables.end()) << "\n"); - auto it = o_calls.find(UpnpEvent_get_SID_cstr(evp)); if (it != o_calls.end()) { (it->second)(evp->ChangedVariables); } else { - LOGINF("Service::srvCB: no callback found for sid " << - UpnpEvent_get_SID_cstr(evp) << endl); + LOGINF("Service::srvCB: no callback found for SID " << sid << "\n"); } break; } @@ -267,7 +264,7 @@ default: // Ignore other events for now LOGDEB("Service:srvCB: unprocessed evt type: [" << - evTypeAsString(et) << "]" << endl); + evTypeAsString(et) << "]" << "\n"); break; } @@ -292,9 +289,6 @@ LOGERR("Service::initEvents: Can't get lib" << endl); return false; } - lib->m->registerHandler(UPNP_EVENT_RENEWAL_COMPLETE, srvCB, 0); - lib->m->registerHandler(UPNP_EVENT_SUBSCRIBE_COMPLETE, srvCB, 0); - lib->m->registerHandler(UPNP_EVENT_UNSUBSCRIBE_COMPLETE, srvCB, 0); lib->m->registerHandler(UPNP_EVENT_AUTORENEWAL_FAILED, srvCB, 0); lib->m->registerHandler(UPNP_EVENT_RECEIVED, srvCB, 0); return true; @@ -308,7 +302,7 @@ LOGINF("Service::subscribe: no lib" << endl); return false; } - int timeout = 1800; + int timeout = lib->m->getSubsTimeout(); int ret = UpnpSubscribe(lib->m->getclh(), eventURL.c_str(), &timeout, SID); if (ret != UPNP_E_SUCCESS) { @@ -341,14 +335,17 @@ return true; } -void Service::registerCallback(evtCBFunc c) +bool Service::registerCallback(evtCBFunc c) { - if (!m || !m->subscribe()) - return; + if (!m || !m->subscribe()) { + LOGERR("registerCallback: subscribe failed\n"); + return false; + } std::unique_lock<std::mutex> lock(cblock); LOGDEB1("Service::registerCallback: " << m->eventURL << " SID " << m->SID << endl); o_calls[m->SID] = c; + return true; } void Service::unregisterCallback() @@ -380,12 +377,12 @@ m->reporter = reporter; } -void Service::reSubscribe() +bool Service::reSubscribe() { LOGDEB("Service::reSubscribe()\n"); if (m->SID[0] == 0) { LOGINF("Service::reSubscribe: no subscription (null SID)\n"); - return; + return false; } evtCBFunc c; { @@ -394,12 +391,13 @@ if (it == o_calls.end()) { LOGINF("Service::reSubscribe: no callback found for m->SID " << m->SID << endl); - return; + return false; } c = it->second; } unregisterCallback(); registerCallback(c); + return true; } template int Service::runSimpleAction<int>(string const&, string const&, int); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/control/service.hxx new/libupnpp-0.21.0/libupnpp/control/service.hxx --- old/libupnpp-0.20.2/libupnpp/control/service.hxx 2020-09-21 09:33:30.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/control/service.hxx 2021-01-03 18:35:45.000000000 +0100 @@ -41,10 +41,16 @@ * Runs in an event thread. This could for example be * implemented by a Qt Object to generate events for the GUI. * - * The Service class does a bit of parsing for common cases. + * This is used by all "precooked" UPnP/AV service classes in the + library. The derived Service class does a bit of parsing for common + cases. * The different methods cover all current types of audio UPnP * state variable data I am aware of. Of course, other types of data can * be reported as a character string, leaving the parsing to the client code. + * + * In the general case, you could also derive from Service, implement + * and install an evtCBFunc callback and not use VarEventReporter at + * all. */ class UPNPP_API VarEventReporter { public: @@ -58,12 +64,16 @@ virtual void changed(const char * /*nm*/, UPnPDirObject /*meta*/) {} /** Special for ohplaylist. Not always needed */ virtual void changed(const char * /*nm*/, std::vector<int> /*ids*/) {} + /** Subscription autorenew failed. You may want to schedule a + resubscribe() later. */ + virtual void autorenew_failed() {} }; /** Type of the event callbacks. * If registered by a call to Service::registerCallBack(cbfunc), this will be * called with a map of state variable names and values when * an event arrives. The call is performed in a separate thread. + * A call with an empty map means that a subscription autorenew failed. */ typedef std::function<void (const std::unordered_map<std::string, std::string>&)> @@ -92,8 +102,9 @@ /** Restart the subscription to get all the State variable values, * in case we get the events before we are ready (e.g. before the - * connections are set in a qt app) */ - virtual void reSubscribe(); + * connections are set in a qt app). Also: when reconnecting after + * a device restarts. */ + virtual bool reSubscribe(); /** Accessors for the values extracted from the device description during * initialization */ @@ -136,7 +147,10 @@ */ virtual VarEventReporter *getReporter(); - /** Install event data reporter object */ + /** Install or uninstall event data reporter object. + * @param reporter the callbacks to be installed, or nullptr + * to disable reporting (and cancel the upnp subscription). + */ virtual void installReporter(VarEventReporter* reporter); /** Perform a comparison to the service type string for this specific @@ -166,7 +180,7 @@ * creates an entry in the static map, using m_SID, which was * obtained by subscribe() during construction */ - void registerCallback(evtCBFunc c); + bool registerCallback(evtCBFunc c); /** To be overridden in classes which actually support events. Will be * called by installReporter(). The call sequence is as follows: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/device/device.cxx new/libupnpp-0.21.0/libupnpp/device/device.cxx --- old/libupnpp-0.20.2/libupnpp/device/device.cxx 2020-12-07 17:57:33.000000000 +0100 +++ new/libupnpp-0.21.0/libupnpp/device/device.cxx 2021-02-09 16:01:04.000000000 +0100 @@ -41,6 +41,12 @@ #include "libupnpp/upnpp_p.hxx" #include "vdir.hxx" +#ifndef NPUPNP_AT_LEAST +#define NPUPNP_AT_LEAST(MAJ,MIN,REV) \ + VERSION_AT_LEAST(NPUPNP_VERSION_MAJOR,NPUPNP_VERSION_MINOR, \ + NPUPNP_VERSION_PATCH, (MAJ),(MIN),(REV)) +#endif + using namespace std; using namespace UPnPP; @@ -71,6 +77,10 @@ UPnPP::LibUPnP *lib{nullptr}; string deviceId; + // For SERVER headers: + string product; + string version; + // In case startloop has been called: the event loop thread. std::thread loopthread; @@ -208,18 +218,29 @@ UpnpDevice::~UpnpDevice() { - shouldExit(); - if (m->loopthread.joinable()) - m->loopthread.join(); + m->needExit = true; + m->evloopcond.notify_all(); - if (nullptr != m->rootdev) { + if (nullptr == m->rootdev) { UpnpUnRegisterRootDevice(m->dvh); } + if (m->loopthread.joinable()) + m->loopthread.join(); + std::unique_lock<std::mutex> lock(o->devices_lock); auto it = o->devices.find(m->deviceId); if (it != o->devices.end()) o->devices.erase(it); + delete m; +} + +void UpnpDevice::setProductVersion(const char *product, const char *version) +{ + if (product && version) { + m->product = product; + m->version = version; + } } const string& UpnpDevice::getDeviceId() const @@ -315,6 +336,11 @@ return false; } #endif +#if NPUPNP_AT_LEAST(4,1,0) + if (!product.empty()) { + UpnpDeviceSetProduct(dvh, product.c_str(), version.c_str()); + } +#endif if ((ret = UpnpSendAdvertisement(dvh, expiretime)) != 0) { LOGERR("UpnpDevice::Internal::start(): sendAvertisement failed: " << lib->errAsString("UpnpDevice: UpnpSendAdvertisement", ret) << @@ -757,6 +783,9 @@ void UpnpDevice::shouldExit() { + if (nullptr == m->rootdev) { + UpnpUnRegisterRootDevice(m->dvh); + } m->needExit = true; m->evloopcond.notify_all(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/device/device.hxx new/libupnpp-0.21.0/libupnpp/device/device.hxx --- old/libupnpp-0.20.2/libupnpp/device/device.hxx 2020-11-07 14:25:04.000000000 +0100 +++ new/libupnpp-0.21.0/libupnpp/device/device.hxx 2021-01-24 18:49:36.000000000 +0100 @@ -39,8 +39,17 @@ /** * Base Device class. * - * The derived class mostly need to implement the readLibFile() method for - * retrieving misc XML description fragments and files. + * This class dispatches the UPnP action calls to the appropriate + * UpnpService derived class methods, and receives, then forwards, the + * UPnP events. + * + * The UpnpService objects attach themselves to the + * UpnpDevice object during their construction and do most of the + * application work. + * + * The derived UPnPDevice classes mostly need to implement the + * readLibFile() method for retrieving misc XML description fragments + * and files. */ class UPNPP_API UpnpDevice { public: @@ -65,7 +74,11 @@ */ UpnpDevice(UpnpDevice *rootdev, const std::string& deviceId); - ~UpnpDevice(); + virtual ~UpnpDevice(); + + /** Set the product name and version to be used in SERVER headers. + * If not set, the library default will be used */ + void setProductVersion(const char *product, const char *version); /** Retrieve the network endpoint the server is listening on */ static bool ipv4(std::string *host, unsigned short *port); @@ -127,28 +140,33 @@ /** * Event-generating loop. * - * This can either be called from the main thread, or in a - * separate thread by a call to startloop - * - * This loop mostly polls the derived class getEventData() method - * and generates an UPnP event if it returns changed variables. + * This only returns if shouldExit() is called from another + * thread. eventloop() can be called from an application thread + * (maybe the main one when program initialisation is + * done). Alternatively, it can be entered through startloop(), + * which will create a thread to run it. + * + * The loop runs every second to call the getEventData() methods + * from the attached UpnpService objects and generates UPnP events + * if they return changed variables. * * The UPnP action calls happen in other (npupnp) threads with * which we synchronize, currently using a global lock. * * Alternatively to running this method, either directly or through - * startloop, it is possible to initially call start() (which returns + * startloop(), it is possible to initially call start() (which returns * after initializing the device with the lower level library), and then - * call notifyEvent() from the application own event loop. + * call notifyEvent() when needed, from the application own event + * loop. No polling occurs in this case, events are pushed from + * the application code. */ void eventloop(); /** * Start a thread to run the event loop and return immediately. * - * This is an alternative to running eventloop() from - * the main thread. The destructor will take care of the internal - * thread. + * This is an alternative to running eventloop() from an application + * thread. The destructor will take care of the internal thread. */ void startloop(); @@ -171,7 +189,7 @@ /** * Register and activate this device. * - * This should only be called if eventloop() is not used, and any events + * This should only be called if eventloop() is not used, and all events * will be generated by calls to notifyEvent(). */ bool start(); @@ -180,8 +198,11 @@ * Generate an event for the service. * * This is mostly useful if eventloop() is not used. + * @param service the service generating the event. + * @param names the names of changed variables. + * @param values the parallel values of the changed variables. */ - void notifyEvent(const UpnpService*, + void notifyEvent(const UpnpService *service, const std::vector<std::string>& names, const std::vector<std::string>& values); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/device/vdir.cxx new/libupnpp-0.21.0/libupnpp/device/vdir.cxx --- old/libupnpp-0.20.2/libupnpp/device/vdir.cxx 2020-09-20 14:05:25.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/device/vdir.cxx 2020-12-31 17:21:30.000000000 +0100 @@ -25,6 +25,7 @@ #include <iostream> #include <utility> #include <unordered_map> +#include <mutex> #include "libupnpp/log.hxx" #include "libupnpp/upnpp_p.hxx" @@ -53,6 +54,7 @@ VirtualDir::FileOps ops; }; static unordered_map<string, DirEnt> m_dirs; +static std::mutex dirsmutex; static void pathcatslash(string& path) { @@ -61,7 +63,7 @@ } } -// Look up entry for pathname +// Look up entry for pathname. Call with lock held static FileEnt *vdgetentry(const char *pathname, DirEnt **de) { //LOGDEB("vdgetentry: [" << pathname << "]" << endl); @@ -103,6 +105,7 @@ //LOGDEB("VirtualDir::addFile: path " << path << " name " << name << endl); + std::lock_guard<std::mutex> lock(dirsmutex); if (m_dirs.find(path) == m_dirs.end()) { m_dirs[path] = DirEnt(); UpnpAddVirtualDir(path.c_str(), 0, 0); @@ -122,6 +125,7 @@ { string path(_path); pathcatslash(path); + std::lock_guard<std::mutex> lock(dirsmutex); if (m_dirs.find(path) == m_dirs.end()) { m_dirs[path] = DirEnt(true); UpnpAddVirtualDir(path.c_str(), 0, 0); @@ -159,6 +163,7 @@ { //LOGDEB("vdgetinfo: [" << fn << "] off_t " << sizeof(off_t) << // " time_t " << sizeof(time_t) << endl); + std::lock_guard<std::mutex> lock(dirsmutex); DirEnt *dir; FileEnt *entry = vdgetentry(fn, &dir); if (dir && dir->isvd) { @@ -191,6 +196,7 @@ const char* fn, enum UpnpOpenFileMode, const void*, const void*) { //LOGDEB("vdopen: " << fn << endl); + std::lock_guard<std::mutex> lock(dirsmutex); DirEnt *dir; FileEnt *entry = vdgetentry(fn, &dir); @@ -214,6 +220,7 @@ const void*, const void*) { // LOGDEB("vdread: " << endl); + std::lock_guard<std::mutex> lock(dirsmutex); if (buflen == 0) { return 0; } @@ -236,6 +243,7 @@ const void*, const void*) { // LOGDEB("vdseek: " << endl); + std::lock_guard<std::mutex> lock(dirsmutex); Handle *h = (Handle *)fileHnd; if (h->vhandle) { return h->dir->ops.seek(h->vhandle, (off_t)offset, origin) == diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/log.cpp new/libupnpp-0.21.0/libupnpp/log.cpp --- old/libupnpp-0.20.2/libupnpp/log.cpp 2020-09-21 10:23:26.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/log.cpp 2020-11-19 11:10:07.000000000 +0100 @@ -44,7 +44,7 @@ if (!m_tocerr && m_stream.is_open()) { m_stream.close(); } - if (!m_fn.empty() && m_fn.compare("stderr")) { + if (!m_fn.empty() && m_fn != "stderr") { m_stream.open(m_fn, std::fstream::out | std::ofstream::trunc); if (!m_stream.is_open()) { cerr << "Logger::Logger: log open failed: for [" << diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/log.h new/libupnpp-0.21.0/libupnpp/log.h --- old/libupnpp-0.20.2/libupnpp/log.h 2020-06-29 10:01:51.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/log.h 2020-11-19 11:10:07.000000000 +0100 @@ -48,7 +48,7 @@ * output. Creates the singleton logger object. Only the first * call changes the state, further ones just return the Logger * pointer. */ - static Logger *getTheLog(const std::string& fn); + static Logger *getTheLog(const std::string& fn = std::string()); /** Close and reopen the output file. For rotating the log: rename * then reopen. */ @@ -80,6 +80,14 @@ int getloglevel() const { return m_loglevel; } + /** @brief Retrieve current log file name */ + const std::string& getlogfilename() const { + return m_fn; + } + /** @brief Logging to stderr ? */ + bool logisstderr() const { + return m_tocerr; + } /** @brief turn date logging on or off (default is off) */ void logthedate(bool onoff) { @@ -121,11 +129,11 @@ Logger& operator=(const Logger &); }; -#define LOGGER_PRT (Logger::getTheLog("")->getstream()) +#define LOGGER_PRT (Logger::getTheLog()->getstream()) #if LOGGER_THREADSAFE #define LOGGER_LOCK \ - std::unique_lock<std::recursive_mutex> lock(Logger::getTheLog("")->getmutex()) + std::unique_lock<std::recursive_mutex> lock(Logger::getTheLog()->getmutex()) #else #define LOGGER_LOCK #endif @@ -134,11 +142,11 @@ #define LOGGER_LOCAL_LOGINC 0 #endif -#define LOGGER_LEVEL (Logger::getTheLog("")->getloglevel() + \ +#define LOGGER_LEVEL (Logger::getTheLog()->getloglevel() + \ LOGGER_LOCAL_LOGINC) -#define LOGGER_DATE (Logger::getTheLog("")->loggingdate() ? \ - Logger::getTheLog("")->datestring() : "") +#define LOGGER_DATE (Logger::getTheLog()->loggingdate() ? \ + Logger::getTheLog()->datestring() : "") #define LOGGER_DOLOG(L,X) LOGGER_PRT << LOGGER_DATE << ":" << L << ":" << \ __FILE__ << ":" << __LINE__ << "::" << X \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/smallut.cpp new/libupnpp-0.21.0/libupnpp/smallut.cpp --- old/libupnpp-0.20.2/libupnpp/smallut.cpp 2020-12-03 10:28:43.000000000 +0100 +++ new/libupnpp-0.21.0/libupnpp/smallut.cpp 2021-02-11 10:01:13.000000000 +0100 @@ -15,15 +15,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ -#ifdef _WIN32 -// needed for localtime_r under mingw. Has to exist before the other -// includes from smallut.h -#define _POSIX_THREAD_SAFE_FUNCTIONS -#ifdef _MSC_VER -#define localtime_r(a,b) localtime_s(b,a) -#endif /* _MSC_VER */ -#endif /* _WIN32 */ - #include "smallut.h" #include <algorithm> @@ -34,6 +25,7 @@ #include <cstdio> #include <cstdlib> #include <cstring> +#include <ctime> #include <iostream> #include <list> #include <numeric> @@ -41,7 +33,13 @@ #include <string> #include <unordered_map> #include <unordered_set> -#include <ctime> + +#ifdef _WIN32 +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#define localtime_r(a,b) localtime_s(b,a) +#endif /* _WIN32 */ + // Older compilers don't support stdc++ regex, but Windows does not // have the Linux one. Have a simple class to solve the simple cases. @@ -262,20 +260,18 @@ template <class T> void stringsToString(const T& tokens, string& s) { - for (auto it = tokens.begin(); - it != tokens.end(); it++) { - bool hasblanks = false; - if (it->find_first_of(" \t\n") != string::npos) { - hasblanks = true; - } - if (it != tokens.begin()) { - s.append(1, ' '); + if (tokens.empty()) + return; + for (const auto& tok : tokens) { + if (tok.empty()) { + s.append("\"\" "); + continue; } + bool hasblanks = tok.find_first_of(" \t\n") != string::npos; if (hasblanks) { s.append(1, '"'); } - for (unsigned int i = 0; i < it->length(); i++) { - char car = it->at(i); + for (auto car : tok) { if (car == '"') { s.append(1, '\\'); s.append(1, car); @@ -286,7 +282,9 @@ if (hasblanks) { s.append(1, '"'); } + s.append(1, ' '); } + s.resize(s.size()-1); } template <class T> string stringsToString(const T& tokens) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/smallut.h new/libupnpp-0.21.0/libupnpp/smallut.h --- old/libupnpp-0.20.2/libupnpp/smallut.h 2020-11-10 14:11:27.000000000 +0100 +++ new/libupnpp-0.21.0/libupnpp/smallut.h 2021-02-09 16:01:16.000000000 +0100 @@ -43,6 +43,13 @@ #ifndef PRETEND_USE #define PRETEND_USE(var) ((void)(var)) #endif +#ifndef VERSION_AT_LEAST +#define VERSION_AT_LEAST(LIBMAJ,LIBMIN,LIBREV,TARGMAJ,TARGMIN,TARGREV) \ + ((LIBMAJ) > (TARGMAJ) || \ + ((LIBMAJ) == (TARGMAJ) && \ + ((LIBMIN) > (TARGMIN) || \ + ((LIBMIN) == (TARGMIN) && (LIBREV) >= (TARGREV))))) +#endif #endif /* SMALLUT_DISABLE_MACROS */ // Case-insensitive compare. ASCII ONLY ! diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/upnpp_p.hxx new/libupnpp-0.21.0/libupnpp/upnpp_p.hxx --- old/libupnpp-0.20.2/libupnpp/upnpp_p.hxx 2020-07-28 13:25:22.000000000 +0200 +++ new/libupnpp-0.21.0/libupnpp/upnpp_p.hxx 2021-01-02 16:18:37.000000000 +0100 @@ -75,6 +75,8 @@ class LibUPnP::Internal { public: + int getSubsTimeout(); + /** Specify function to be called on given UPnP * event. The call will happen in the libupnp thread context. */ @@ -92,7 +94,6 @@ }; int setupWebServer(const std::string& description, UpnpDevice_Handle *dvh); - UpnpClient_Handle getclh(); bool ok; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/upnpplib.cxx new/libupnpp-0.21.0/libupnpp/upnpplib.cxx --- old/libupnpp-0.20.2/libupnpp/upnpplib.cxx 2020-11-14 10:23:04.000000000 +0100 +++ new/libupnpp-0.21.0/libupnpp/upnpplib.cxx 2021-03-01 15:17:38.000000000 +0100 @@ -62,7 +62,9 @@ std::string ifnames; std::string ipv4; int port; - + int substimeout{1800}; + std::string clientproduct; + std::string clientversion; }; static UPnPOptions options; @@ -92,6 +94,15 @@ case UPNPPINIT_OPTION_PORT: options.port = va_arg(ap, int); break; + case UPNPPINIT_OPTION_SUBSCRIPTION_TIMEOUT: + options.substimeout = va_arg(ap, int); + break; + case UPNPPINIT_OPTION_CLIENT_PRODUCT: + options.clientproduct = *((std::string*)(va_arg(ap, std::string*))); + break; + case UPNPPINIT_OPTION_CLIENT_VERSION: + options.clientversion = *((std::string*)(va_arg(ap, std::string*))); + break; default: std::cerr << "LibUPnP::init: unknown option value " << option <<"\n"; } @@ -186,6 +197,11 @@ return addr; } +int LibUPnP::Internal::getSubsTimeout() +{ + return options.substimeout; +} + LibUPnP::LibUPnP() { bool serveronly = 0 != (options.flags&UPNPPINIT_FLAG_SERVERONLY); @@ -224,9 +240,14 @@ } setMaxContentLength(2000*1024); +#ifdef UPNP_ENABLE_IPV6 LOGINF("LibUPnP: Using IPV4 " << UpnpGetServerIpAddress() << " port " << UpnpGetServerPort() << " IPV6 " << UpnpGetServerIp6Address() << " port " << UpnpGetServerPort6() << endl); +#else + LOGINF("LibUPnP: Using IPV4 " << UpnpGetServerIpAddress() << " port " << + UpnpGetServerPort() << endl); +#endif // Client initialization is simple, just do it. Defer device // initialization because it's more complicated. @@ -236,6 +257,12 @@ m->init_error = UpnpRegisterClient(o_callback, (void *)this, &m->clh); if (m->init_error == UPNP_E_SUCCESS) { +#if NPUPNP_VERSION >= 40100 + if (!options.clientproduct.empty()&&!options.clientversion.empty()) { + UpnpClientSetProduct(m->clh, options.clientproduct.c_str(), + options.clientversion.c_str()); + } +#endif m->ok = true; } else { LOGERR(errAsString("UpnpRegisterClient", m->init_error) << endl); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/upnpplib.hxx new/libupnpp-0.21.0/libupnpp/upnpplib.hxx --- old/libupnpp-0.20.2/libupnpp/upnpplib.hxx 2020-12-30 15:06:13.000000000 +0100 +++ new/libupnpp-0.21.0/libupnpp/upnpplib.hxx 2021-01-18 19:05:20.000000000 +0100 @@ -25,8 +25,8 @@ /** Version components. */ #define LIBUPNPP_VERSION_MAJOR 0 -#define LIBUPNPP_VERSION_MINOR 20 -#define LIBUPNPP_VERSION_REVISION 2 +#define LIBUPNPP_VERSION_MINOR 21 +#define LIBUPNPP_VERSION_REVISION 0 /// Got this from Xapian... #define LIBUPNPP_AT_LEAST(A,B,C) \ (LIBUPNPP_VERSION_MAJOR > (A) || \ @@ -59,8 +59,8 @@ /** Retrieve the singleton LibUPnP object. * - * Using this call with arguments is deprecated. Call init() then - * getLibUPnP() without arguments instead. + * Using this call with arguments is deprecated. Call init() + * (creates the lib) then getLibUPnP() without arguments instead. * * This initializes libupnp, possibly setting an address and port, possibly * registering a client if serveronly is false. @@ -84,32 +84,46 @@ const std::string ip = std::string(), unsigned short port = 0); + /** Configuration flags for the initialisation call */ enum InitFlags { UPNPPINIT_FLAG_NONE = 0, + /** Disable IPV6 support */ UPNPPINIT_FLAG_NOIPV6 = 1, + /** Do not initialize the client side (we are a device) */ UPNPPINIT_FLAG_SERVERONLY = 2, }; + /** Options for the initialisation call. Each option argument may be + * followed by specific parameters. */ enum InitOption { /** Terminate the VARARGs list. */ UPNPPINIT_OPTION_END = 0, - /** Names of the interfaces to use. Space-separated list. If not - * set, we will use the first interface. If set to '*', will use - * all. const std::string* follows. */ + /** Names of the interfaces to use. A const std::string* follows. + * This is a space-separated list. If not + * set, we will use the first interface. If set to '*', we will use + * all possible interfaces. */ UPNPPINIT_OPTION_IFNAMES, - /** Use single IPV4 address. This is incompatible with OPTION_IFNAMES. - * const std::string* address in dot notation follows. */ + /** Use single IPV4 address. A const std::string* address in + * dot notation follows. This is incompatible with OPTION_IFNAMES. */ UPNPPINIT_OPTION_IPV4, - /** IP Port to use. The lower lib default is 49152. - * int follows */ + /** IP Port to use. An int parameter follows. The lower lib default + * is 49152. */ UPNPPINIT_OPTION_PORT, + /** Control: subscription timeout in seconds. An int param. follows. */ + UPNPPINIT_OPTION_SUBSCRIPTION_TIMEOUT, + /** Control: product name to set in user-agent strings. + * A const std::string* follows. */ + UPNPPINIT_OPTION_CLIENT_PRODUCT, + /** Control: product version to set in user-agent strings. + * A const std::string* follows. */ + UPNPPINIT_OPTION_CLIENT_VERSION, }; /** Initialize the library. * * On success you will then call getLibUPnP() to access the object instance. - * @param flags OR'd InitFlags. - * @param ... A list of InitOption and values, ended by UPNPPINIT_OPTION_END. + * @param flags A bitfield of @ref InitFlags values. + * @param ... A list of @ref InitOption and values, ended by UPNPPINIT_OPTION_END. * @return false for failure, true for success. */ static bool init(unsigned int flags, ...); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libupnpp-0.20.2/libupnpp/workqueue.h new/libupnpp-0.21.0/libupnpp/workqueue.h --- old/libupnpp-0.20.2/libupnpp/workqueue.h 2020-11-26 09:26:06.000000000 +0100 +++ new/libupnpp-0.21.0/libupnpp/workqueue.h 2021-01-01 22:36:00.000000000 +0100 @@ -72,6 +72,16 @@ } } + /** Task deleter + * If put() is called with the flush option, and the tasks allocate memory, + * you need to set this function, which will be called on each task popped + * from the queue. Tasks which go through normally must be freed by the + * worker function. + */ + void setTaskFreeFunc(void (*func)(T&)) { + m_taskfreefunc = func; + } + /** Start the worker threads. * * @param nworkers number of threads copies to start. @@ -120,6 +130,10 @@ } if (flushprevious) { while (!m_queue.empty()) { + if (m_taskfreefunc) { + T& d = m_queue.front(); + m_taskfreefunc(d); + } m_queue.pop(); } } @@ -325,6 +339,7 @@ #endif }; + void (*m_taskfreefunc)(T&){nullptr}; // Configuration std::string m_name; size_t m_high;
