Hello community, here is the log from the commit of package noson for openSUSE:Factory checked in at 2019-01-24 14:13:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/noson (Old) and /work/SRC/openSUSE:Factory/.noson.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "noson" Thu Jan 24 14:13:51 2019 rev:3 rq:667840 version:1.10.4 Changes: -------- --- /work/SRC/openSUSE:Factory/noson/noson.changes 2018-08-04 21:55:01.621459719 +0200 +++ /work/SRC/openSUSE:Factory/.noson.new.28833/noson.changes 2019-01-24 14:13:53.143329858 +0100 @@ -1,0 +2,14 @@ +Mon Jan 21 17:14:29 UTC 2019 - bo...@opensuse.org + +- Update to 1.10.4 + * Add fallback to parse malformed xml + * Refactor secure socket and fix SNI issue + * Add functions to handles bass and treble levels + * Add functions to provides the protocol identifier from an URI + * Fix the parse of alarm recurrence + * Remove unused builtin functions since standard C++98 is no + longer supported + * Improve SMAPI interoperability + * Improve OAuth registration for third part services + +------------------------------------------------------------------- Old: ---- 1.8.2.tar.gz New: ---- 1.10.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ noson.spec ++++++ --- /var/tmp/diff_new_pack.Kmj223/_old 2019-01-24 14:13:53.683329234 +0100 +++ /var/tmp/diff_new_pack.Kmj223/_new 2019-01-24 14:13:53.687329231 +0100 @@ -1,7 +1,7 @@ # # spec file for package noson # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: noson -Version: 1.8.2 +Version: 1.10.4 Release: 0 Summary: C++ library for accessing sonos devices License: GPL-3.0-or-later ++++++ 1.8.2.tar.gz -> 1.10.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/README.md new/noson-1.10.4/README.md --- old/noson-1.8.2/README.md 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/README.md 2019-01-10 10:19:26.000000000 +0100 @@ -11,18 +11,15 @@ ### Linux, BSD, OSX -Start by creating a build folder -<pre><code>mkdir -p build -rm -rf build/* -cd build/</code></pre> - -To build execute the following: -<pre><code>cmake -DCMAKE_BUILD_TYPE=Release .. -make</code></pre> +Configure, make and install + +<pre><code>cmake -DCMAKE_BUILD_TYPE=Release $NOSON_PROJECT_DIR +make +sudo make install</code></pre> ### Windows -Start by installing VC strudio 2012 and CMAKE tool +Start by installing VC studio 2012 and CMAKE tool To build open a command tool CMD.EXE from the project folder and execute the following <pre><code>mkdir build_vc @@ -36,5 +33,7 @@ ## Generate the documentation +sudo apt-get install graphviz + doxygen <root path of noson>/docs/doxygen-dev.cfg diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/docs/doxygen-dev.cfg new/noson-1.10.4/docs/doxygen-dev.cfg --- old/noson-1.8.2/docs/doxygen-dev.cfg 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/docs/doxygen-dev.cfg 2019-01-10 10:19:26.000000000 +0100 @@ -686,7 +686,7 @@ # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = ../noson/src/private # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/CMakeLists.txt new/noson-1.10.4/noson/CMakeLists.txt --- old/noson-1.8.2/noson/CMakeLists.txt 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/CMakeLists.txt 2019-01-10 10:19:26.000000000 +0100 @@ -14,15 +14,11 @@ OPTION (STATIC_CRT "Link the static CRT libraries" OFF) endif () -if (NOT MSVC) - OPTION (REQUIRE_CXX_98 "Require standard c++98" OFF) -endif () - ############################################################################### # set lib version here set (noson_VERSION_MAJOR 1) -set (noson_VERSION_MINOR 8) -set (noson_VERSION_PATCH 2) +set (noson_VERSION_MINOR 10) +set (noson_VERSION_PATCH 4) set (noson_VERSION ${noson_VERSION_MAJOR}.${noson_VERSION_MINOR}.${noson_VERSION_PATCH}) set (NOSON_LIB_VERSION ${noson_VERSION}) @@ -53,23 +49,10 @@ include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) - if(NOT REQUIRE_CXX_98 AND COMPILER_SUPPORTS_CXX11) + if(COMPILER_SUPPORTS_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - message(STATUS "Using standard c++11") else() - CHECK_CXX_COMPILER_FLAG("-std=c++98" COMPILER_SUPPORTS_CXX98) - if(COMPILER_SUPPORTS_CXX98) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++98") - message(STATUS "Using standard c++98") - endif() - include (CheckLibraryExists) - include (CheckAtomic) - if (HAS_BUILTIN_SYNC_ADD_AND_FETCH) - add_definitions ("-DHAS_BUILTIN_SYNC_ADD_AND_FETCH") - endif () - if (HAS_BUILTIN_SYNC_SUB_AND_FETCH) - add_definitions ("-DHAS_BUILTIN_SYNC_SUB_AND_FETCH") - endif () + message(FATAL_ERROR "CXX compiler doesn't support standard c++11") endif () endif () diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/alarm.cpp new/noson-1.10.4/noson/src/alarm.cpp --- old/noson-1.8.2/noson/src/alarm.cpp 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/alarm.cpp 2019-01-10 10:19:26.000000000 +0100 @@ -179,7 +179,7 @@ unsigned lon = strlen(RecurrenceTable[Recurrence_ON]); if (it->length() > lon && it->substr(0, lon) == RecurrenceTable[Recurrence_ON]) { - std::string days = it->substr(lon + 1, std::string::npos); + std::string days = it->substr(lon, std::string::npos); for (unsigned i = 0; i < days.length(); ++i) { char dno = days[i] - 0x30; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/musicservices.cpp new/noson-1.10.4/noson/src/musicservices.cpp --- old/noson-1.8.2/noson/src/musicservices.cpp 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/musicservices.cpp 2019-01-10 10:19:26.000000000 +0100 @@ -28,6 +28,8 @@ #include "private/wsresponse.h" #include "private/os/threads/mutex.h" +#define USER_AGENT "Linux UPnP/1.0 Sonos/36.4-41270 (ACR_noson)" + using namespace NSROOT; const std::string MusicServices::Name("MusicServices"); @@ -200,9 +202,8 @@ // store new value of version versionPtr->assign(vars.GetValue("AvailableServiceListVersion")); std::string agent; - //@FIXME make the user agent string according to the template: Linux UPnP/1.0 Sonos/26.99-12345 - //Resolved by SoCo: https://github.com/SoCo/SoCo/blob/18ee1ec11bba8463c4536aa7c2a25f5c20a051a4/soco/music_services/music_service.py#L55 - agent.assign("Linux UPnP/1.0 Sonos/26.99-12345"); + // configure a valid user-agent + agent.assign(USER_AGENT); // Fill the list of services. for (std::vector<ElementList>::const_iterator it = data.begin(); it != data.end(); ++it) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/private/builtin.h new/noson-1.10.4/noson/src/private/builtin.h --- old/noson-1.8.2/noson/src/private/builtin.h 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/private/builtin.h 2019-01-10 10:19:26.000000000 +0100 @@ -55,48 +55,6 @@ #define string_to_uint8 __str2uint8 extern int string_to_uint8(const char *str, uint8_t *num); -#define int64_to_string __int64str -static CC_INLINE void int64_to_string(int64_t num, char *str) -{ - sprintf(str, "%lld", (long long)num); -} - -#define int32_to_string __int32str -static CC_INLINE void int32_to_string(int32_t num, char *str) -{ - sprintf(str, "%ld", (long)num); -} - -#define int16_to_string __int16str -static CC_INLINE void int16_to_string(int16_t num, char *str) -{ - sprintf(str, "%d", num); -} - -#define int8_to_string __int8str -static CC_INLINE void int8_to_string(int8_t num, char *str) -{ - sprintf(str, "%d", num); -} - -#define uint32_to_string __uint32str -static CC_INLINE void uint32_to_string(uint32_t num, char *str) -{ - sprintf(str, "%lu", (unsigned long)num); -} - -#define uint16_to_string __uint16str -static CC_INLINE void uint16_to_string(uint16_t num, char *str) -{ - sprintf(str, "%u", num); -} - -#define uint8_to_string __uint8str -static CC_INLINE void uint8_to_string(uint8_t num, char *str) -{ - sprintf(str, "%u", num); -} - #define TIMESTAMP_UTC_LEN (sizeof("YYYY-MM-DDTHH:MM:SSZ") - 1) #define TIMESTAMP_LEN (sizeof("YYYY-MM-DDTHH:MM:SS") - 1) #define DATESTAMP_LEN (sizeof("YYYY-MM-DD") - 1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/private/securesocket.cpp new/noson-1.10.4/noson/src/private/securesocket.cpp --- old/noson-1.8.2/noson/src/private/securesocket.cpp 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/private/securesocket.cpp 2019-01-10 10:19:26.000000000 +0100 @@ -34,14 +34,6 @@ #define ERRNO_INTR EINTR #endif /* __WINDOWS__ */ -#if HAVE_OPENSSL -#include <openssl/ssl.h> -#include <openssl/err.h> -#include <openssl/pem.h> -#include <openssl/x509.h> -#include <openssl/x509_vfy.h> -#endif - using namespace NSROOT; SSLSessionFactory* SSLSessionFactory::m_instance = 0; @@ -58,51 +50,80 @@ SAFE_DELETE(m_instance); } +#if HAVE_OPENSSL + +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/x509.h> +#include <openssl/x509_vfy.h> + +/* Cipher suites, https://www.openssl.org/docs/apps/ciphers.html */ +const char* const PREFERRED_CIPHERS = "HIGH:!aNULL:!kRSA:!SRP:!PSK:!CAMELLIA:!RC4:!MD5:!DSS"; + SSLSessionFactory::SSLSessionFactory() : m_enabled(false) , m_ctx(NULL) { -#if HAVE_OPENSSL - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - ERR_load_crypto_strings(); if (SSL_library_init() < 0) DBG(DBG_ERROR, "%s: could not initialize the SSL library\n", __FUNCTION__); else { - if ((m_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) + SSL_load_error_strings(); + /* SSL_load_error_strings loads both libssl and libcrypto strings */ + /* ERR_load_crypto_strings(); */ + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + m_ctx = SSL_CTX_new(TLS_client_method()); +#else + m_ctx = SSL_CTX_new(SSLv23_client_method()); +#endif + if (m_ctx == NULL) DBG(DBG_ERROR, "%s: could not create the SSL context\n", __FUNCTION__); else { - m_enabled = true; SSL_CTX_set_verify(static_cast<SSL_CTX*>(m_ctx), SSL_VERIFY_NONE, 0); + + /* Remove the most egregious. Because SSLv2 and SSLv3 have been removed, + * a TLSv1.0 handshake is used. The client accepts TLSv1.0 and above. + * An added benefit of TLS 1.0 and above are TLS extensions like Server + * Name Indicatior (SNI). + */ + const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION; + (void)SSL_CTX_set_options(static_cast<SSL_CTX*>(m_ctx), flags); + + /* Each cipher suite takes 2 bytes in the ClientHello, so advertising every + * cipher suite available at the client is going to cause a big ClientHello + * (or bigger then needed to get the job done). + * When using SSL_CTX_set_cipher_list or SSL_set_cipher_list with the string + * below you'll cut the number of cipher suites down to about 45. + */ + if (SSL_CTX_set_cipher_list(static_cast<SSL_CTX*>(m_ctx), PREFERRED_CIPHERS) != 1) + DBG(DBG_ERROR, "%s: Set cipher list failed\n", __FUNCTION__); + + /* The SSL trace callback is only used for verbose logging */ + /* SSL_CTX_set_msg_callback(static_cast<SSL_CTX*>(m_ctx), ssl_trace); */ + DBG(DBG_INFO, "%s: SSL engine initialized\n", __FUNCTION__); + m_enabled = true; } } -#else - DBG(DBG_INFO, "%s: SSL library disabled\n", __FUNCTION__); -#endif } SSLSessionFactory::~SSLSessionFactory() { -#if HAVE_OPENSSL if (m_ctx) SSL_CTX_free(static_cast<SSL_CTX*>(m_ctx)); ERR_free_strings(); EVP_cleanup(); DBG(DBG_INFO, "%s: SSL resources destroyed\n", __FUNCTION__); -#endif } -SecureSocket* SSLSessionFactory::NewSocket(bool disableSSLv2 /*= true*/) +SecureSocket* SSLSessionFactory::NewSocket() { if (m_enabled) { -#if HAVE_OPENSSL SSL* ssl = SSL_new(static_cast<SSL_CTX*>(m_ctx)); - if (disableSSLv2) - SSL_set_options(static_cast<SSL*>(ssl), SSL_OP_NO_SSLv2); /* SSL_MODE_AUTO_RETRY * With this option set, if the server suddenly wants a new handshake, * OpenSSL handles it in the background. Without this option, any read @@ -111,9 +132,6 @@ */ SSL_set_mode(static_cast<SSL*>(ssl), SSL_MODE_AUTO_RETRY); return new SecureSocket(ssl); -#else - (void)disableSSLv2; -#endif } return NULL; } @@ -129,24 +147,25 @@ SecureSocket::~SecureSocket() { -#if HAVE_OPENSSL Disconnect(); SSL_free(static_cast<SSL*>(m_ssl)); -#endif } bool SecureSocket::Connect(const char* server, unsigned port, int rcvbuf) { m_ssl_error = 0; -#if HAVE_OPENSSL if (m_connected) Disconnect(); - // Connect the tcp socket to the server + + /* Connect the tcp socket to the server */ if (!TcpSocket::Connect(server, port, rcvbuf)) return false; - // setup ssl + + /* setup SSL */ SSL_set_fd(static_cast<SSL*>(m_ssl), m_socket); - // try SSL handshake + SSL_set_tlsext_host_name(static_cast<SSL*>(m_ssl), server); /* fix SNI */ + + /* do SSL handshake */ for (;;) { int r = SSL_connect(static_cast<SSL*>(m_ssl)); @@ -168,7 +187,7 @@ } DBG(DBG_PROTO, "%s: SSL handshake initialized\n", __FUNCTION__); m_connected = true; - // check for a valid certificate + /* check for a valid certificate */ std::string str(""); if (!IsCertificateValid(str)) { @@ -177,17 +196,10 @@ } DBG(DBG_PROTO, "%s: %s\n", __FUNCTION__, str.c_str()); return true; -#else - (void)server; - (void)port; - (void)rcvbuf; -#endif - return false; } size_t SecureSocket::ReceiveData(void* buf, size_t n) { -#if HAVE_OPENSSL if (m_connected && n > 0) { m_ssl_error = SSL_ERROR_NONE; @@ -234,16 +246,11 @@ break; } } -#else - (void)buf; - (void)n; -#endif return 0; } bool SecureSocket::SendData(const char* buf, size_t size) { -#if HAVE_OPENSSL if (m_connected && size > 0) { m_ssl_error = SSL_ERROR_NONE; @@ -270,16 +277,11 @@ break; } } -#else - (void)buf; - (void)size; -#endif return false; } void SecureSocket::Disconnect() { -#if HAVE_OPENSSL if (m_connected) { SSL_shutdown(static_cast<SSL*>(m_ssl)); @@ -291,7 +293,6 @@ X509_free(static_cast<X509*>(m_cert)); m_cert = NULL; } -#endif } bool SecureSocket::IsValid() const @@ -301,7 +302,6 @@ bool SecureSocket::IsCertificateValid(std::string& str) { -#if HAVE_OPENSSL if (m_cert) X509_free(static_cast<X509*>(m_cert)); m_cert = SSL_get_peer_certificate(static_cast<SSL*>(m_ssl)); @@ -314,8 +314,75 @@ str.assign(X509_NAME_oneline(name, buf, sizeof(buf) - 1)); return true; } + return false; +} + #else + +SSLSessionFactory::SSLSessionFactory() +: m_enabled(false) +, m_ctx(NULL) +{ + DBG(DBG_INFO, "%s: SSL feature is disabled\n", __FUNCTION__); +} + +SSLSessionFactory::~SSLSessionFactory() +{ +} + +SecureSocket* SSLSessionFactory::NewSocket() +{ + return new SecureSocket(NULL); +} + +SecureSocket::SecureSocket(void* ssl) +: TcpSocket() +, m_ssl(ssl) +, m_cert(NULL) +, m_connected(false) +, m_ssl_error(0) +{ +} + +SecureSocket::~SecureSocket() +{ +} + +bool SecureSocket::Connect(const char* server, unsigned port, int rcvbuf) +{ + (void)server; + (void)port; + (void)rcvbuf; + return false; +} + +size_t SecureSocket::ReceiveData(void* buf, size_t n) +{ + (void)buf; + (void)n; + return 0; +} + +bool SecureSocket::SendData(const char* buf, size_t size) +{ + (void)buf; + (void)size; + return false; +} + +void SecureSocket::Disconnect() +{ +} + +bool SecureSocket::IsValid() const +{ + return m_connected; +} + +bool SecureSocket::IsCertificateValid(std::string& str) +{ (void)str; -#endif return false; } + +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/private/securesocket.h new/noson-1.10.4/noson/src/private/securesocket.h --- old/noson-1.8.2/noson/src/private/securesocket.h 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/private/securesocket.h 2019-01-10 10:19:26.000000000 +0100 @@ -36,7 +36,7 @@ static SSLSessionFactory& Instance(); static void Destroy(); bool isEnabled() const { return m_enabled; } - SecureSocket* NewSocket(bool disableSSLv2 = true); + SecureSocket* NewSocket(); private: SSLSessionFactory(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/private/xmldict.h new/noson-1.10.4/noson/src/private/xmldict.h --- old/noson-1.8.2/noson/src/private/xmldict.h 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/private/xmldict.h 2019-01-10 10:19:26.000000000 +0100 @@ -88,8 +88,10 @@ XMLNS* FindName(const char* name); const XMLNS* FindName(const char* name) const; - private: typedef std::list<XMLNS> XMLNSList; + XMLNSList List() { return m_names; } + + private: XMLNSList m_names; }; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/renderingcontrol.cpp new/noson-1.10.4/noson/src/renderingcontrol.cpp --- old/noson-1.8.2/noson/src/renderingcontrol.cpp 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/renderingcontrol.cpp 2019-01-10 10:19:26.000000000 +0100 @@ -142,6 +142,57 @@ return false; } +bool RenderingControl::GetTreble(int8_t* value) +{ + ElementList args; + args.push_back(ElementPtr(new Element("InstanceID", "0"))); + ElementList vars = Request("GetTreble", args); + if (!vars.empty() && vars[0]->compare("GetTrebleResponse") == 0) + { + ElementList::const_iterator it = vars.FindKey("CurrentTreble"); + if (it != vars.end()) + return (string_to_int8((*it)->c_str(), value) == 0); + } + return false; +} + +bool RenderingControl::SetTreble(int8_t value) +{ + ElementList args; + args.push_back(ElementPtr(new Element("InstanceID", "0"))); + args.push_back(ElementPtr(new Element("DesiredTreble", std::to_string(value)))); + ElementList vars = Request("SetTreble", args); + if (!vars.empty() && vars[0]->compare("SetTrebleResponse") == 0) + return true; + return false; +} + +bool RenderingControl::GetBass(int8_t* value) +{ + ElementList args; + args.push_back(ElementPtr(new Element("InstanceID", "0"))); + ElementList vars = Request("GetBass", args); + if (!vars.empty() && vars[0]->compare("GetBassResponse") == 0) + { + ElementList::const_iterator it = vars.FindKey("CurrentBass"); + if (it != vars.end()) { + return (string_to_int8((*it)->c_str(), value) == 0); + } + } + return false; +} + +bool RenderingControl::SetBass(int8_t value) +{ + ElementList args; + args.push_back(ElementPtr(new Element("InstanceID", "0"))); + args.push_back(ElementPtr(new Element("DesiredBass", std::to_string(value)))); + ElementList vars = Request("SetBass", args); + if (!vars.empty() && vars[0]->compare("SetBassResponse") == 0) + return true; + return false; +} + void RenderingControl::HandleEventMessage(EventMessagePtr msg) { if (!msg) @@ -192,6 +243,16 @@ if (string_to_int32((*++it).c_str(), &num) == 0) prop->NightMode = num; } + else if (*it == "Bass") + { + if (string_to_int32((*++it).c_str(), &num) == 0) + prop->Bass = num; + } + else if (*it == "Treble") + { + if (string_to_int32((*++it).c_str(), &num) == 0) + prop->Treble = num; + } ++it; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/renderingcontrol.h new/noson-1.10.4/noson/src/renderingcontrol.h --- old/noson-1.8.2/noson/src/renderingcontrol.h 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/renderingcontrol.h 2019-01-10 10:19:26.000000000 +0100 @@ -67,6 +67,14 @@ bool SetNightmode(uint8_t value); + bool GetTreble(int8_t* value); + + bool SetTreble(int8_t value); + + bool GetBass(int8_t* value); + + bool SetBass(int8_t value); + // Implements EventSubscriber virtual void HandleEventMessage(EventMessagePtr msg); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/service.cpp new/noson-1.10.4/noson/src/service.cpp --- old/noson-1.8.2/noson/src/service.cpp 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/service.cpp 2019-01-10 10:19:26.000000000 +0100 @@ -35,6 +35,17 @@ using namespace NSROOT; +namespace NSROOT +{ + void __traceServiceError(tinyxml2::XMLDocument& doc) + { + DBG(DBG_ERROR, "%s: invalid or not supported response\n", __FUNCTION__); + tinyxml2::XMLPrinter out; + doc.Accept(&out); + DBG(DBG_ERROR, "%s\n", out.CStr()); + } +} + Service::Service(const std::string& serviceHost, unsigned servicePort) : m_host(serviceHost) , m_port(servicePort) @@ -107,14 +118,22 @@ } const tinyxml2::XMLElement* elem; // an element // Check for response: Envelope/Body/{respTag} - if (!(elem = rootdoc.RootElement()) || !XMLNS::NameEqual(elem->Name(), "Envelope") || - !(elem = elem->FirstChildElement()) || !XMLNS::NameEqual(elem->Name(), "Body") || - !(elem = elem->FirstChildElement())) + if (!(elem = rootdoc.RootElement()) || !XMLNS::NameEqual(elem->Name(), "Envelope")) { - DBG(DBG_ERROR, "%s: invalid or not supported response\n", __FUNCTION__); - tinyxml2::XMLPrinter out; - rootdoc.Accept(&out); - DBG(DBG_ERROR, "%s\n", out.CStr()); + __traceServiceError(rootdoc); + SetFault(vars); + return vars; + } + + // search the element 'Body' + elem = elem->FirstChildElement(); + while (elem && !XMLNS::NameEqual(elem->Name(), "Body")) + elem = elem->NextSiblingElement(NULL); + + if (!elem || !(elem = elem->FirstChildElement())) + { + __traceServiceError(rootdoc); + SetFault(vars); return vars; } vars.push_back(ElementPtr(new Element("TAG", XMLNS::LocalName(elem->Name())))); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/smapi.cpp new/noson-1.10.4/noson/src/smapi.cpp --- old/noson-1.8.2/noson/src/smapi.cpp 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/smapi.cpp 2019-01-10 10:19:26.000000000 +0100 @@ -47,7 +47,7 @@ } XMLDict SMAPIDict = __initSMAPIDict(); - void __dumpInvalidResponse(tinyxml2::XMLDocument& doc) + void __traceSMAPIError(tinyxml2::XMLDocument& doc) { DBG(DBG_ERROR, "%s: invalid or not supported response\n", __FUNCTION__); tinyxml2::XMLPrinter out; @@ -275,7 +275,7 @@ const tinyxml2::XMLElement* elem = rootdoc.RootElement(); if (!elem || !(elem = elem->FirstChildElement(NULL))) { - __dumpInvalidResponse(rootdoc); + __traceSMAPIError(rootdoc); return false; } while (elem) @@ -291,7 +291,7 @@ string_to_uint16(m_service->GetPolicy()->GetAttribut("PollInterval").c_str(), &poll); if (!m_authLinkTimeout) m_authLinkTimeout = new OS::CTimeout(); - m_authLinkTimeout->Set(poll * 1000); + m_authLinkTimeout->Set((poll < 60 ? 60 : poll) * 1000); m_authLinkCode = vars.GetValue("linkCode"); m_authLinkDeviceId = vars.GetValue("linkDeviceId"); regUrl = vars.GetValue("regUrl"); @@ -322,21 +322,21 @@ const tinyxml2::XMLElement* elem = rootdoc.RootElement(); if (!elem || !(elem = elem->FirstChildElement(NULL))) { - __dumpInvalidResponse(rootdoc); + __traceSMAPIError(rootdoc); return false; } while (elem && !XMLNS::NameEqual(elem->Name(), "authorizeAccount")) elem = elem->NextSiblingElement(NULL); if (!elem || !(elem = elem->FirstChildElement(NULL))) { - __dumpInvalidResponse(rootdoc); + __traceSMAPIError(rootdoc); return false; } while (elem && !XMLNS::NameEqual(elem->Name(), "deviceLink")) elem = elem->NextSiblingElement(NULL); if (!elem || !(elem = elem->FirstChildElement(NULL))) { - __dumpInvalidResponse(rootdoc); + __traceSMAPIError(rootdoc); return false; } while (elem) @@ -377,7 +377,11 @@ const std::string& tag = resp.GetValue("TAG"); if (tag == "Fault") + { + // show the feedback and return true to request a retry + DBG(DBG_INFO, "%s: %s\n", __FUNCTION__, m_fault.GetValue("faultstring").c_str()); return true; + } else if (tag == "getDeviceAuthTokenResponse") { const std::string& data = resp.GetValue("getDeviceAuthTokenResult"); @@ -391,7 +395,7 @@ const tinyxml2::XMLElement* elem = rootdoc.RootElement(); if (!elem || !(elem = elem->FirstChildElement(NULL))) { - __dumpInvalidResponse(rootdoc); + __traceSMAPIError(rootdoc); return false; } while (elem) @@ -421,6 +425,14 @@ return false; } +const std::string& SMAPI::GetFaultString() const +{ + OS::CLockGuard lock(*m_mutex); + if (m_fault.GetValue("TAG") == "Fault") + return m_fault.GetValue("faultstring"); + return m_fault.GetValue("errorstring"); +} + bool SMAPI::parsePresentationMap(const std::string& xml) { tinyxml2::XMLDocument rootdoc; @@ -606,6 +618,7 @@ if (rootdoc.Parse(data.c_str(), len) != tinyxml2::XML_SUCCESS) { DBG(DBG_ERROR, "%s: parse xml failed\n", __FUNCTION__); + SetFault(vars); return vars; } const tinyxml2::XMLElement* elem; // an element @@ -613,17 +626,23 @@ if (!(elem = rootdoc.RootElement()) || !XMLNS::NameEqual(elem->Name(), "Envelope")) { - __dumpInvalidResponse(rootdoc); + __traceSMAPIError(rootdoc); + SetFault(vars); return vars; } // learn declared namespaces in the element Envelope for translations XMLNames xmlnames; xmlnames.AddXMLNS(elem); - if (!(elem = elem->FirstChildElement()) || !XMLNS::NameEqual(elem->Name(), "Body") || - !(elem = elem->FirstChildElement())) + // search the element 'Body' + elem = elem->FirstChildElement(); + while (elem && !XMLNS::NameEqual(elem->Name(), "Body")) + elem = elem->NextSiblingElement(NULL); + + if (!elem || !(elem = elem->FirstChildElement())) { - __dumpInvalidResponse(rootdoc); + __traceSMAPIError(rootdoc); + SetFault(vars); return vars; } vars.push_back(ElementPtr(new Element("TAG", XMLNS::LocalName(elem->Name())))); @@ -664,14 +683,18 @@ { if (elem->GetText()) { - vars.push_back(ElementPtr(new Element(SMAPIDict.TranslateQName(xmlnames, elem->Name()), elem->GetText()))); + // Some services supply malformed xml with undefined namespace and so translating qualified name will fail. + //vars.push_back(ElementPtr(new Element(SMAPIDict.TranslateQName(xmlnames, elem->Name()), elem->GetText()))); + vars.push_back(ElementPtr(new Element(XMLNS::LocalName(elem->Name()), elem->GetText()))); DBG(DBG_PROTO, "%s: %s = %s\n", __FUNCTION__, vars.back()->GetKey().c_str(), vars.back()->c_str()); } else if (!elem->NoChildren()) { tinyxml2::XMLPrinter out; elem->Accept(&out); - vars.push_back(ElementPtr(new Element(SMAPIDict.TranslateQName(xmlnames, elem->Name()), out.CStr()))); + // Some services supply malformed xml with undefined namespace and so translating qualified name will fail. + //vars.push_back(ElementPtr(new Element(SMAPIDict.TranslateQName(xmlnames, elem->Name()), out.CStr()))); + vars.push_back(ElementPtr(new Element(XMLNS::LocalName(elem->Name()), out.CStr()))); DBG(DBG_PROTO, "%s: dump (%s)\n%s\n", __FUNCTION__, vars.back()->GetKey().c_str(), vars.back()->c_str()); } elem = elem->NextSiblingElement(NULL); @@ -723,16 +746,17 @@ // Retry the request vars = DoCall(action, args); } - else if (XMLNS::NameEqual(str.c_str(), "Client.AuthTokenExpired") && !m_authTokenExpired) - { - m_authTokenExpired = true; - makeSoapHeader(); // refresh hearder - } - // handle others fault like Client.SessionIdInvalid, Client.LoginInvalid else if (!m_authTokenExpired) { - m_authTokenExpired = true; - makeSoapHeader(); // refresh header; + if (XMLNS::NameEqual(str.c_str(), "Client.AuthTokenExpired") || + XMLNS::NameEqual(str.c_str(), "Client.LoginDisabled") || + XMLNS::NameEqual(str.c_str(), "Client.LoginInvalid") || + XMLNS::NameEqual(str.c_str(), "Client.LoginUnauthorized") || + XMLNS::NameEqual(str.c_str(), "Client.SessionIdInvalid")) + { + m_authTokenExpired = true; + makeSoapHeader(); // refresh hearder + } } } return vars; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/smapi.h new/noson-1.10.4/noson/src/smapi.h --- old/noson-1.8.2/noson/src/smapi.h 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/smapi.h 2019-01-10 10:19:26.000000000 +0100 @@ -118,6 +118,12 @@ */ bool GetDeviceAuthToken(SMOAKeyring::Data& auth); + /** + * Returns the message of fault + * @return the fault string + */ + const std::string& GetFaultString() const; + private: OS::CMutex* m_mutex; PlayerPtr m_player; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/smapimetadata.cpp new/noson-1.10.4/noson/src/smapimetadata.cpp --- old/noson-1.8.2/noson/src/smapimetadata.cpp 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/smapimetadata.cpp 2019-01-10 10:19:26.000000000 +0100 @@ -56,8 +56,11 @@ m_list.clear(); m_startIndex = m_itemCount = m_totalCount = 0; m_root.assign(root); - if (m_service) + // don't parse an empty content + if (!xml.empty() && m_service) m_valid = ParseMessage(xml); + else + m_valid = false; } SMAPIItemList SMAPIMetadata::GetItems() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/sonosplayer.cpp new/noson-1.10.4/noson/src/sonosplayer.cpp --- old/noson-1.8.2/noson/src/sonosplayer.cpp 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/sonosplayer.cpp 2019-01-10 10:19:26.000000000 +0100 @@ -394,6 +394,46 @@ return false; } +bool Player::GetBass(const std::string &uuid, int8_t* value) +{ + for (RCTable::const_iterator it = m_RCTable.begin(); it != m_RCTable.end(); ++it) + { + if (it->uuid == uuid) + return it->renderingControl->GetBass(value); + } + return false; +} + +bool Player::SetBass(const std::string &uuid, int8_t value) +{ + for (RCTable::const_iterator it = m_RCTable.begin(); it != m_RCTable.end(); ++it) + { + if (it->uuid == uuid) + return it->renderingControl->SetBass(value); + } + return false; +} + +bool Player::GetTreble(const std::string &uuid, int8_t* value) +{ + for (RCTable::const_iterator it = m_RCTable.begin(); it != m_RCTable.end(); ++it) + { + if (it->uuid == uuid) + return it->renderingControl->GetTreble(value); + } + return false; +} + +bool Player::SetTreble(const std::string &uuid, int8_t value) +{ + for (RCTable::const_iterator it = m_RCTable.begin(); it != m_RCTable.end(); ++it) + { + if (it->uuid == uuid) + return it->renderingControl->SetTreble(value); + } + return false; +} + bool Player::SetCurrentURI(const DigitalItemPtr& item) { // Fix items from 'My radios' haven't required tag desc @@ -768,3 +808,15 @@ } return itemId; } + +Protocol_t Player::GetURIProtocol(const std::string& uri) +{ + size_t pos = uri.find_first_of(":"); + if (pos == std::string::npos) + return Protocol_unknown; + std::string proto = uri.substr(0, pos); + int i = 0; + while (i < Protocol_unknown && proto != ProtocolTable[i]) + ++i; + return static_cast<Protocol_t>(i); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/sonosplayer.h new/noson-1.10.4/noson/src/sonosplayer.h --- old/noson-1.8.2/noson/src/sonosplayer.h 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/sonosplayer.h 2019-01-10 10:19:26.000000000 +0100 @@ -111,6 +111,12 @@ bool GetNightmode(const std::string& uuid, uint8_t* value); bool SetNightmode(const std::string& uuid, uint8_t value); + bool GetBass(const std::string& uuid, int8_t* value); + bool SetBass(const std::string& uuid, int8_t value); + + bool GetTreble(const std::string& uuid, int8_t* value); + bool SetTreble(const std::string& uuid, int8_t value); + bool SetCurrentURI(const DigitalItemPtr& item); bool PlayStream(const std::string& streamURL, const std::string& title); bool PlayQueue(bool start); @@ -158,6 +164,8 @@ // Helpers std::string GetItemIdFromUriMetadata(const DigitalItemPtr& uriMetadata); + Protocol_t GetURIProtocol(const std::string& uri); + private: bool m_valid; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/sonossystem.cpp new/noson-1.10.4/noson/src/sonossystem.cpp --- old/noson-1.8.2/noson/src/sonossystem.cpp 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/sonossystem.cpp 2019-01-10 10:19:26.000000000 +0100 @@ -311,7 +311,7 @@ // on first call we fill the container requesting sonos service if ((*ccPtr)++ == 0 && !LoadMSLogo(logos)) - DBG(DBG_ERROR, "%s: cache for service images cannot be filled", __FUNCTION__); + DBG(DBG_ERROR, "%s: cache for service images cannot be filled\n", __FUNCTION__); const std::string& typeId = service->GetServiceType(); for (ElementList::const_iterator it = logos.begin(); it != logos.end(); ++it) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/noson/src/sonostypes.h new/noson-1.10.4/noson/src/sonostypes.h --- old/noson-1.8.2/noson/src/sonostypes.h 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/noson/src/sonostypes.h 2019-01-10 10:19:26.000000000 +0100 @@ -184,6 +184,8 @@ , MuteLF(0) , MuteRF(0) , NightMode(0) + , Treble(0) + , Bass(0) { } virtual ~RCSProperty() { } @@ -195,6 +197,8 @@ int MuteLF; int MuteRF; int NightMode; + int Treble; + int Bass; }; class SRProperty diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/noson-1.8.2/test/src/test.cpp new/noson-1.10.4/test/src/test.cpp --- old/noson-1.8.2/test/src/test.cpp 2018-07-22 23:32:32.000000000 +0200 +++ new/noson-1.10.4/test/src/test.cpp 2019-01-10 10:19:26.000000000 +0100 @@ -139,7 +139,8 @@ while (it != bdir.end()) { PRINT4("Item %d: [%d] [%s] [%s]\n", ++i, (*it)->IsItem(), (*it)->GetValue("dc:title").c_str(), (*it)->GetObjectID().c_str()); - PRINT3(" %d: %s, %s\n", i, (*it)->GetValue("res").c_str(), (*it)->GetProperty("res")->GetAttribut("protocolInfo").c_str()); + if ((*it)->GetProperty("res")) + PRINT3(" %d: %s, %s\n", i, (*it)->GetValue("res").c_str(), (*it)->GetProperty("res")->GetAttribut("protocolInfo").c_str()); SONOS::DigitalItemPtr payload; if (SONOS::System::ExtractObjectFromFavorite(*it, payload)) PRINT2(" F %d: %s\n", i, payload->GetObjectID().c_str());