diff --git a/src/db/plugins/upnp/ContentDirectoryService.cxx b/src/db/plugins/upnp/ContentDirectoryService.cxx index d98559c7b..f94f9de41 100644 --- a/src/db/plugins/upnp/ContentDirectoryService.cxx +++ b/src/db/plugins/upnp/ContentDirectoryService.cxx @@ -25,6 +25,7 @@ #include "Directory.hxx" #include "util/NumberParser.hxx" #include "util/UriUtil.hxx" +#include "util/RuntimeError.hxx" #include "util/Error.hxx" #include @@ -59,21 +60,16 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl, "SortCriteria", "", "StartingIndex", ofbuf, "RequestedCount", cntbuf); - if (request == nullptr) { - error.Set(upnp_domain, "UpnpMakeAction() failed"); - return false; - } + if (request == nullptr) + throw std::runtime_error("UpnpMakeAction() failed"); IXML_Document *response; int code = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(), 0 /*devUDN*/, request, &response); ixmlDocument_free(request); - 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)); const char *value = ixmlwrap::getFirstElementValue(response, "NumberReturned"); didreadp = value != nullptr @@ -129,22 +125,17 @@ ContentDirectoryService::search(UpnpClient_Handle hdl, "SortCriteria", "", "StartingIndex", ofbuf, "RequestedCount", "0"); // Setting a value here gets twonky into fits - if (request == 0) { - error.Set(upnp_domain, "UpnpMakeAction() failed"); - return false; - } + if (request == 0) + throw std::runtime_error("UpnpMakeAction() failed"); IXML_Document *response; auto code = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(), 0 /*devUDN*/, request, &response); ixmlDocument_free(request); - 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)); const char *value = ixmlwrap::getFirstElementValue(response, "NumberReturned"); @@ -182,22 +173,17 @@ ContentDirectoryService::getMetadata(UpnpClient_Handle hdl, "SortCriteria", "", "StartingIndex", "0", "RequestedCount", "1"); - if (request == nullptr) { - error.Set(upnp_domain, "UpnpMakeAction() failed"); - return false; - } + if (request == nullptr) + throw std::runtime_error("UpnpMakeAction() failed"); IXML_Document *response; auto code = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(), 0 /*devUDN*/, request, &response); ixmlDocument_free(request); - 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)); bool success = ReadResultTag(dirbuf, response, error); ixmlDocument_free(response); diff --git a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx index f69bfd08f..bca25efaa 100644 --- a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx +++ b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx @@ -179,16 +179,17 @@ UpnpDatabase::Configure(const ConfigBlock &, Error &) } bool -UpnpDatabase::Open(Error &error) +UpnpDatabase::Open(gcc_unused Error &error) { - if (!UpnpClientGlobalInit(handle, error)) - return false; + UpnpClientGlobalInit(handle); discovery = new UPnPDeviceDirectory(handle); - if (!discovery->Start(error)) { + try { + discovery->Start(); + } catch (...) { delete discovery; UpnpClientGlobalFinish(); - return false; + throw; } return true; @@ -216,15 +217,11 @@ const LightSong * UpnpDatabase::GetSong(const char *uri, Error &error) const { auto vpath = stringToTokens(uri, "/", true); - if (vpath.size() < 2) { - error.Format(db_domain, (int)DatabaseErrorCode::NOT_FOUND, - "No such song: %s", uri); - return nullptr; - } + if (vpath.size() < 2) + throw DatabaseError(DatabaseErrorCode::NOT_FOUND, + "No such song"); - ContentDirectoryService server; - if (!discovery->GetServer(vpath.front().c_str(), server, error)) - return nullptr; + auto server = discovery->GetServer(vpath.front().c_str()); vpath.pop_front(); @@ -276,10 +273,7 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server, if (selection.filter == nullptr) return true; - std::list searchcaps; - if (!server.getSearchCapabilities(handle, searchcaps, error)) - return false; - + const auto searchcaps = server.getSearchCapabilities(handle); if (searchcaps.empty()) return true; @@ -434,13 +428,10 @@ UpnpDatabase::ReadNode(const ContentDirectoryService &server, if (!server.getMetadata(handle, objid, dirbuf, error)) return false; - if (dirbuf.objects.size() == 1) { - dirent = std::move(dirbuf.objects.front()); - } else { - error.Format(upnp_domain, "Bad resource"); - return false; - } + if (dirbuf.objects.size() != 1) + throw std::runtime_error("Bad resource"); + dirent = std::move(dirbuf.objects.front()); return true; } @@ -495,24 +486,18 @@ UpnpDatabase::Namei(const ContentDirectoryService &server, // Look for the name in the sub-container list UPnPDirObject *child = dirbuf.FindObject(i->c_str()); - if (child == nullptr) { - error.Format(db_domain, - (int)DatabaseErrorCode::NOT_FOUND, - "No such object"); - return false; - } + if (child == nullptr) + throw DatabaseError(DatabaseErrorCode::NOT_FOUND, + "No such object"); if (i == last) { odirent = std::move(*child); return true; } - if (child->type != UPnPDirObject::Type::CONTAINER) { - error.Format(db_domain, - (int)DatabaseErrorCode::NOT_FOUND, - "Not a container"); - return false; - } + if (child->type != UPnPDirObject::Type::CONTAINER) + throw DatabaseError(DatabaseErrorCode::NOT_FOUND, + "Not a container"); objid = std::move(child->id); } @@ -607,10 +592,8 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server, break; default: - error.Format(db_domain, - (int)DatabaseErrorCode::NOT_FOUND, - "Not found"); - return false; + throw DatabaseError(DatabaseErrorCode::NOT_FOUND, + "Not found"); } if (visit_song) { @@ -620,12 +603,9 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server, return false; if (dirent.type != UPnPDirObject::Type::ITEM || - dirent.item_class != UPnPDirObject::ItemClass::MUSIC) { - error.Format(db_domain, - (int)DatabaseErrorCode::NOT_FOUND, - "Not found"); - return false; - } + dirent.item_class != UPnPDirObject::ItemClass::MUSIC) + throw DatabaseError(DatabaseErrorCode::NOT_FOUND, + "Not found"); std::string path = songPath(server.getFriendlyName(), dirent.id); @@ -693,11 +673,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection, { auto vpath = stringToTokens(selection.uri, "/", true); if (vpath.empty()) { - std::vector servers; - if (!discovery->GetDirectories(servers, error)) - return false; - - for (const auto &server : servers) { + for (const auto &server : discovery->GetDirectories()) { if (visit_directory) { const LightDirectory d(server.getFriendlyName(), 0); if (!visit_directory(d, error)) @@ -718,10 +694,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection, std::string servername(std::move(vpath.front())); vpath.pop_front(); - ContentDirectoryService server; - if (!discovery->GetServer(servername.c_str(), server, error)) - return false; - + auto server = discovery->GetServer(servername.c_str()); return VisitServer(server, vpath, selection, visit_directory, visit_song, visit_playlist, error); } @@ -737,12 +710,8 @@ UpnpDatabase::VisitUniqueTags(const DatabaseSelection &selection, if (!visit_tag) return true; - std::vector servers; - if (!discovery->GetDirectories(servers, error)) - return false; - std::set values; - for (auto& server : servers) { + for (auto& server : discovery->GetDirectories()) { UPnPDirContent dirbuf; if (!SearchSongs(server, rootid, selection, dirbuf, error)) return false; diff --git a/src/lib/upnp/ClientInit.cxx b/src/lib/upnp/ClientInit.cxx index 5558ee8d6..46b1a71bf 100644 --- a/src/lib/upnp/ClientInit.cxx +++ b/src/lib/upnp/ClientInit.cxx @@ -23,10 +23,12 @@ #include "Callback.hxx" #include "Domain.hxx" #include "thread/Mutex.hxx" -#include "util/Error.hxx" +#include "util/RuntimeError.hxx" #include +#include + 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 diff --git a/src/lib/upnp/ClientInit.hxx b/src/lib/upnp/ClientInit.hxx index f49f255ee..153bb1516 100644 --- a/src/lib/upnp/ClientInit.hxx +++ b/src/lib/upnp/ClientInit.hxx @@ -24,10 +24,8 @@ #include -class Error; - -bool -UpnpClientGlobalInit(UpnpClient_Handle &handle, Error &error); +void +UpnpClientGlobalInit(UpnpClient_Handle &handle); void UpnpClientGlobalFinish(); diff --git a/src/lib/upnp/ContentDirectoryService.cxx b/src/lib/upnp/ContentDirectoryService.cxx index 8509300b3..8ff192fb8 100644 --- a/src/lib/upnp/ContentDirectoryService.cxx +++ b/src/lib/upnp/ContentDirectoryService.cxx @@ -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 &result, - Error &error) const +std::list +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 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; } diff --git a/src/lib/upnp/ContentDirectoryService.hxx b/src/lib/upnp/ContentDirectoryService.hxx index bf6ab913a..cbce65696 100644 --- a/src/lib/upnp/ContentDirectoryService.hxx +++ b/src/lib/upnp/ContentDirectoryService.hxx @@ -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 &result, - Error &error) const; + std::list getSearchCapabilities(UpnpClient_Handle handle) const; gcc_pure std::string GetURI() const { diff --git a/src/lib/upnp/Discovery.cxx b/src/lib/upnp/Discovery.cxx index 6a761004d..009ca89ad 100644 --- a/src/lib/upnp/Discovery.cxx +++ b/src/lib/upnp/Discovery.cxx @@ -24,6 +24,7 @@ #include "system/Clock.hxx" #include "Log.hxx" #include "util/ScopeExit.hxx" +#include "util/RuntimeError.hxx" #include @@ -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 &out, - Error &error) +std::vector +UPnPDeviceDirectory::GetDirectories() { const ScopeLock protect(mutex); - if (!ExpireDevices(error)) - return false; + ExpireDevices(); + std::vector 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 &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"); } diff --git a/src/lib/upnp/Discovery.hxx b/src/lib/upnp/Discovery.hxx index 94c93c678..4ee08dd55 100644 --- a/src/lib/upnp/Discovery.hxx +++ b/src/lib/upnp/Discovery.hxx @@ -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 &, Error &); + std::vector 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); diff --git a/src/lib/upnp/Init.cxx b/src/lib/upnp/Init.cxx index 1b471f53d..3dc762525 100644 --- a/src/lib/upnp/Init.cxx +++ b/src/lib/upnp/Init.cxx @@ -21,44 +21,40 @@ #include "Init.hxx" #include "Domain.hxx" #include "thread/Mutex.hxx" -#include "util/Error.hxx" +#include "util/RuntimeError.hxx" #include #include #include +#include + 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 diff --git a/src/lib/upnp/Init.hxx b/src/lib/upnp/Init.hxx index 796251862..133a4fc75 100644 --- a/src/lib/upnp/Init.hxx +++ b/src/lib/upnp/Init.hxx @@ -22,10 +22,8 @@ #include "check.h" -class Error; - -bool -UpnpGlobalInit(Error &error); +void +UpnpGlobalInit(); void UpnpGlobalFinish(); diff --git a/src/neighbor/plugins/UpnpNeighborPlugin.cxx b/src/neighbor/plugins/UpnpNeighborPlugin.cxx index 3c0cb8843..e4b53d762 100644 --- a/src/neighbor/plugins/UpnpNeighborPlugin.cxx +++ b/src/neighbor/plugins/UpnpNeighborPlugin.cxx @@ -70,17 +70,19 @@ private: }; bool -UpnpNeighborExplorer::Open(Error &error) +UpnpNeighborExplorer::Open(gcc_unused Error &error) { UpnpClient_Handle handle; - if (!UpnpClientGlobalInit(handle, error)) - return false; + UpnpClientGlobalInit(handle); discovery = new UPnPDeviceDirectory(handle, this); - if (!discovery->Start(error)) { + + try { + discovery->Start(); + } catch (...) { delete discovery; UpnpClientGlobalFinish(); - return false; + throw; } return true; @@ -98,10 +100,10 @@ UpnpNeighborExplorer::GetList() const { std::vector tmp; - { - Error error; - if (!discovery->GetDirectories(tmp, error)) - LogError(error); + try { + tmp = discovery->GetDirectories(); + } catch (const std::runtime_error &e) { + LogError(e); } List result;