lib/upnp: use C++ exceptions instead of class Error
This commit is contained in:
@@ -23,10 +23,12 @@
|
||||
#include "Callback.hxx"
|
||||
#include "Domain.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
#include <upnp/upnptools.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static Mutex upnp_client_init_mutex;
|
||||
static unsigned upnp_client_ref;
|
||||
static UpnpClient_Handle upnp_client_handle;
|
||||
@@ -44,40 +46,32 @@ UpnpClientCallback(Upnp_EventType et, void *evp, void *cookie)
|
||||
return callback.Invoke(et, evp);
|
||||
}
|
||||
|
||||
static bool
|
||||
DoInit(Error &error)
|
||||
static void
|
||||
DoInit()
|
||||
{
|
||||
auto code = UpnpRegisterClient(UpnpClientCallback, nullptr,
|
||||
&upnp_client_handle);
|
||||
if (code != UPNP_E_SUCCESS) {
|
||||
error.Format(upnp_domain, code,
|
||||
"UpnpRegisterClient() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (code != UPNP_E_SUCCESS)
|
||||
throw FormatRuntimeError("UpnpRegisterClient() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
}
|
||||
|
||||
bool
|
||||
UpnpClientGlobalInit(UpnpClient_Handle &handle, Error &error)
|
||||
void
|
||||
UpnpClientGlobalInit(UpnpClient_Handle &handle)
|
||||
{
|
||||
if (!UpnpGlobalInit(error))
|
||||
return false;
|
||||
UpnpGlobalInit();
|
||||
|
||||
bool success;
|
||||
{
|
||||
try {
|
||||
const ScopeLock protect(upnp_client_init_mutex);
|
||||
success = upnp_client_ref > 0 || DoInit(error);
|
||||
if (upnp_client_ref == 0)
|
||||
DoInit();
|
||||
} catch (...) {
|
||||
UpnpGlobalFinish();
|
||||
throw;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
++upnp_client_ref;
|
||||
handle = upnp_client_handle;
|
||||
} else
|
||||
UpnpGlobalFinish();
|
||||
|
||||
return success;
|
||||
++upnp_client_ref;
|
||||
handle = upnp_client_handle;
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -24,10 +24,8 @@
|
||||
|
||||
#include <upnp/upnp.h>
|
||||
|
||||
class Error;
|
||||
|
||||
bool
|
||||
UpnpClientGlobalInit(UpnpClient_Handle &handle, Error &error);
|
||||
void
|
||||
UpnpClientGlobalInit(UpnpClient_Handle &handle);
|
||||
|
||||
void
|
||||
UpnpClientGlobalFinish();
|
||||
|
@@ -26,7 +26,7 @@
|
||||
#include "Util.hxx"
|
||||
#include "Action.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
ContentDirectoryService::ContentDirectoryService(const UPnPDevice &device,
|
||||
const UPnPService &service)
|
||||
@@ -50,43 +50,34 @@ ContentDirectoryService::~ContentDirectoryService()
|
||||
/* this destructor exists here just so it won't get inlined */
|
||||
}
|
||||
|
||||
bool
|
||||
ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl,
|
||||
std::list<std::string> &result,
|
||||
Error &error) const
|
||||
std::list<std::string>
|
||||
ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
|
||||
{
|
||||
assert(result.empty());
|
||||
|
||||
UniqueIxmlDocument request(UpnpMakeAction("GetSearchCapabilities", m_serviceType.c_str(),
|
||||
0,
|
||||
nullptr, nullptr));
|
||||
if (!request) {
|
||||
error.Set(upnp_domain, "UpnpMakeAction() failed");
|
||||
return false;
|
||||
}
|
||||
if (!request)
|
||||
throw std::runtime_error("UpnpMakeAction() failed");
|
||||
|
||||
IXML_Document *_response;
|
||||
auto code = UpnpSendAction(hdl, m_actionURL.c_str(),
|
||||
m_serviceType.c_str(),
|
||||
0 /*devUDN*/, request.get(), &_response);
|
||||
if (code != UPNP_E_SUCCESS) {
|
||||
error.Format(upnp_domain, code,
|
||||
"UpnpSendAction() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
return false;
|
||||
}
|
||||
if (code != UPNP_E_SUCCESS)
|
||||
throw FormatRuntimeError("UpnpSendAction() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
|
||||
UniqueIxmlDocument response(_response);
|
||||
|
||||
std::list<std::string> result;
|
||||
|
||||
const char *s = ixmlwrap::getFirstElementValue(response.get(),
|
||||
"SearchCaps");
|
||||
if (s == nullptr || *s == 0)
|
||||
return true;
|
||||
return result;
|
||||
|
||||
if (!csvToStrings(s, result)) {
|
||||
error.Set(upnp_domain, "Bad response");
|
||||
return false;
|
||||
}
|
||||
if (!csvToStrings(s, result))
|
||||
throw std::runtime_error("Bad response");
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
@@ -114,13 +114,13 @@ public:
|
||||
Error &error) const;
|
||||
|
||||
/** Retrieve search capabilities
|
||||
*
|
||||
* Throws std::runtime_error on error.
|
||||
*
|
||||
* @param[out] result an empty vector: no search, or a single '*' element:
|
||||
* any tag can be used in a search, or a list of usable tag names.
|
||||
*/
|
||||
bool getSearchCapabilities(UpnpClient_Handle handle,
|
||||
std::list<std::string> &result,
|
||||
Error &error) const;
|
||||
std::list<std::string> getSearchCapabilities(UpnpClient_Handle handle) const;
|
||||
|
||||
gcc_pure
|
||||
std::string GetURI() const {
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "system/Clock.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
#include <upnp/upnptools.h>
|
||||
|
||||
@@ -209,8 +210,8 @@ UPnPDeviceDirectory::Invoke(Upnp_EventType et, void *evp)
|
||||
return UPNP_E_SUCCESS;
|
||||
}
|
||||
|
||||
bool
|
||||
UPnPDeviceDirectory::ExpireDevices(Error &error)
|
||||
void
|
||||
UPnPDeviceDirectory::ExpireDevices()
|
||||
{
|
||||
const unsigned now = MonotonicClockS();
|
||||
bool didsomething = false;
|
||||
@@ -226,9 +227,7 @@ UPnPDeviceDirectory::ExpireDevices(Error &error)
|
||||
}
|
||||
|
||||
if (didsomething)
|
||||
return Search(error);
|
||||
|
||||
return true;
|
||||
Search();
|
||||
}
|
||||
|
||||
UPnPDeviceDirectory::UPnPDeviceDirectory(UpnpClient_Handle _handle,
|
||||
@@ -245,56 +244,45 @@ UPnPDeviceDirectory::~UPnPDeviceDirectory()
|
||||
/* this destructor exists here just so it won't get inlined */
|
||||
}
|
||||
|
||||
bool
|
||||
UPnPDeviceDirectory::Start(Error &error)
|
||||
void
|
||||
UPnPDeviceDirectory::Start()
|
||||
{
|
||||
if (!queue.start(1, Explore, this)) {
|
||||
error.Set(upnp_domain, "Discover work queue start failed");
|
||||
return false;
|
||||
}
|
||||
if (!queue.start(1, Explore, this))
|
||||
throw std::runtime_error("Discover work queue start failed");
|
||||
|
||||
return Search(error);
|
||||
Search();
|
||||
}
|
||||
|
||||
bool
|
||||
UPnPDeviceDirectory::Search(Error &error)
|
||||
void
|
||||
UPnPDeviceDirectory::Search()
|
||||
{
|
||||
const unsigned now = MonotonicClockS();
|
||||
if (now - last_search < 10)
|
||||
return true;
|
||||
return;
|
||||
last_search = now;
|
||||
|
||||
// We search both for device and service just in case.
|
||||
int code = UpnpSearchAsync(handle, search_timeout,
|
||||
ContentDirectorySType, GetUpnpCookie());
|
||||
if (code != UPNP_E_SUCCESS) {
|
||||
error.Format(upnp_domain, code,
|
||||
"UpnpSearchAsync() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
return false;
|
||||
}
|
||||
if (code != UPNP_E_SUCCESS)
|
||||
throw FormatRuntimeError("UpnpSearchAsync() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
|
||||
code = UpnpSearchAsync(handle, search_timeout,
|
||||
MediaServerDType, GetUpnpCookie());
|
||||
if (code != UPNP_E_SUCCESS) {
|
||||
error.Format(upnp_domain, code,
|
||||
"UpnpSearchAsync() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (code != UPNP_E_SUCCESS)
|
||||
throw FormatRuntimeError("UpnpSearchAsync() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
}
|
||||
|
||||
bool
|
||||
UPnPDeviceDirectory::GetDirectories(std::vector<ContentDirectoryService> &out,
|
||||
Error &error)
|
||||
std::vector<ContentDirectoryService>
|
||||
UPnPDeviceDirectory::GetDirectories()
|
||||
{
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
if (!ExpireDevices(error))
|
||||
return false;
|
||||
ExpireDevices();
|
||||
|
||||
std::vector<ContentDirectoryService> out;
|
||||
for (auto dit = directories.begin();
|
||||
dit != directories.end(); dit++) {
|
||||
for (const auto &service : dit->device.services) {
|
||||
@@ -304,18 +292,15 @@ UPnPDeviceDirectory::GetDirectories(std::vector<ContentDirectoryService> &out,
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return out;
|
||||
}
|
||||
|
||||
bool
|
||||
UPnPDeviceDirectory::GetServer(const char *friendly_name,
|
||||
ContentDirectoryService &server,
|
||||
Error &error)
|
||||
ContentDirectoryService
|
||||
UPnPDeviceDirectory::GetServer(const char *friendly_name)
|
||||
{
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
if (!ExpireDevices(error))
|
||||
return false;
|
||||
ExpireDevices();
|
||||
|
||||
for (const auto &i : directories) {
|
||||
const auto &device = i.device;
|
||||
@@ -323,15 +308,11 @@ UPnPDeviceDirectory::GetServer(const char *friendly_name,
|
||||
if (device.friendlyName != friendly_name)
|
||||
continue;
|
||||
|
||||
for (const auto &service : device.services) {
|
||||
if (isCDService(service.serviceType.c_str())) {
|
||||
server = ContentDirectoryService(device,
|
||||
service);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (const auto &service : device.services)
|
||||
if (isCDService(service.serviceType.c_str()))
|
||||
return ContentDirectoryService(device,
|
||||
service);
|
||||
}
|
||||
|
||||
error.Set(upnp_domain, "Server not found");
|
||||
return false;
|
||||
throw std::runtime_error("Server not found");
|
||||
}
|
||||
|
@@ -120,20 +120,18 @@ public:
|
||||
UPnPDeviceDirectory(const UPnPDeviceDirectory &) = delete;
|
||||
UPnPDeviceDirectory& operator=(const UPnPDeviceDirectory &) = delete;
|
||||
|
||||
bool Start(Error &error);
|
||||
void Start();
|
||||
|
||||
/** Retrieve the directory services currently seen on the network */
|
||||
bool GetDirectories(std::vector<ContentDirectoryService> &, Error &);
|
||||
std::vector<ContentDirectoryService> GetDirectories();
|
||||
|
||||
/**
|
||||
* Get server by friendly name.
|
||||
*/
|
||||
bool GetServer(const char *friendly_name,
|
||||
ContentDirectoryService &server,
|
||||
Error &error);
|
||||
ContentDirectoryService GetServer(const char *friendly_name);
|
||||
|
||||
private:
|
||||
bool Search(Error &error);
|
||||
void Search();
|
||||
|
||||
/**
|
||||
* Look at the devices and get rid of those which have not
|
||||
@@ -142,7 +140,7 @@ private:
|
||||
*
|
||||
* Caller must lock #mutex.
|
||||
*/
|
||||
bool ExpireDevices(Error &error);
|
||||
void ExpireDevices();
|
||||
|
||||
void LockAdd(ContentDirectoryDescriptor &&d);
|
||||
void LockRemove(const std::string &id);
|
||||
|
@@ -21,44 +21,40 @@
|
||||
#include "Init.hxx"
|
||||
#include "Domain.hxx"
|
||||
#include "thread/Mutex.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
#include <upnp/upnp.h>
|
||||
#include <upnp/upnptools.h>
|
||||
#include <upnp/ixml.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static Mutex upnp_init_mutex;
|
||||
static unsigned upnp_ref;
|
||||
|
||||
static bool
|
||||
DoInit(Error &error)
|
||||
static void
|
||||
DoInit()
|
||||
{
|
||||
auto code = UpnpInit(0, 0);
|
||||
if (code != UPNP_E_SUCCESS) {
|
||||
error.Format(upnp_domain, code,
|
||||
"UpnpInit() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
return false;
|
||||
}
|
||||
if (code != UPNP_E_SUCCESS)
|
||||
throw FormatRuntimeError("UpnpInit() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
|
||||
UpnpSetMaxContentLength(2000*1024);
|
||||
|
||||
// Servers sometimes make error (e.g.: minidlna returns bad utf-8)
|
||||
ixmlRelaxParser(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
UpnpGlobalInit(Error &error)
|
||||
void
|
||||
UpnpGlobalInit()
|
||||
{
|
||||
const ScopeLock protect(upnp_init_mutex);
|
||||
|
||||
if (upnp_ref == 0 && !DoInit(error))
|
||||
return false;
|
||||
if (upnp_ref == 0)
|
||||
DoInit();
|
||||
|
||||
++upnp_ref;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -22,10 +22,8 @@
|
||||
|
||||
#include "check.h"
|
||||
|
||||
class Error;
|
||||
|
||||
bool
|
||||
UpnpGlobalInit(Error &error);
|
||||
void
|
||||
UpnpGlobalInit();
|
||||
|
||||
void
|
||||
UpnpGlobalFinish();
|
||||
|
Reference in New Issue
Block a user