Commit eb7bde60 authored by Adrien Béraud's avatar Adrien Béraud

upnp: download xml doc using std::future

The UpnpDownloadXmlDoc is now called using shared_future. The api
function is a blocking call and there were instances where,
depending on the router and wifi speeds, the function might fail
or hang. By using std::shared_future, if such a thing arises, the
program won't block.

Change-Id: Ibca8d926e8980ebd4519f0e6bbd6ce2125396159
parent fc251a5d
......@@ -97,7 +97,7 @@ NatPmp::~NatPmp()
}
void
NatPmp::clearIGDs()
NatPmp::clearIgds()
{
// Lock valid IGD.
std::lock_guard<std::mutex> lock(validIgdMutex_);
......@@ -113,7 +113,7 @@ NatPmp::clearIGDs()
}
void
NatPmp::searchForIGD()
NatPmp::searchForIgd()
{
// Lock valid IGD.
std::lock_guard<std::mutex> lock(validIgdMutex_);
......
......@@ -55,10 +55,10 @@ public:
Type getType() const override { return Type::NAT_PMP; }
// Notifies a change in network.
void clearIGDs() override;
void clearIgds() override;
// Renew pmp_igd.
void searchForIGD() override;
void searchForIgd() override;
// Tries to add mapping. Assumes mutex is already locked.
Mapping addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, PortType type, UPnPProtocol::UpnpError& upnp_error) override;
......
......@@ -2,7 +2,8 @@
* Copyright (C) 2004-2019 Savoir-faire Linux Inc.
*
* Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
* Author: Eden Abitbol <eden.abitbol@savoirfairelinux.com>
* Author: Eden Abitbol <eden.abitbol@savoirfairelinux.com>
* Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -21,6 +22,8 @@
#include "pupnp.h"
#include <opendht/thread_pool.h>
namespace jami { namespace upnp {
// Helper functions for xml parsing.
......@@ -80,7 +83,64 @@ errorOnResponse(IXML_Document* doc)
PUPnP::PUPnP()
{
pupnpThread_ = std::thread([this] { registerClientAsync(); });
pupnpThread_ = std::thread([this] {
std::unique_lock<std::mutex> lk(ctrlptMutex_);
while (pupnpRun_) {
pupnpCv_.wait(lk);
if (not clientRegistered_) {
// Register Upnp control point.
int upnp_err = UpnpRegisterClient(ctrlPtCallback, this, &ctrlptHandle_);
if (upnp_err != UPNP_E_SUCCESS)
JAMI_ERR("PUPnP: Can't register client: %s", UpnpGetErrorMessage(upnp_err));
else
clientRegistered_ = true;
}
if (not pupnpRun_)
break;
if (clientRegistered_ and searchForIgd_) {
// Send out search for multiple types of devices, as some routers may possibly only reply to one.
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_ROOT_DEVICE, this);
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_IGD_DEVICE, this);
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANIP_SERVICE, this);
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANPPP_SERVICE, this);
// Reset variable.
searchForIgd_ = false;
}
if (clientRegistered_) {
auto xmlList = std::move(dwnldlXmlList_);
decltype(xmlList) finished {};
// Wait on futures asynchronously
lk.unlock();
for (auto it = xmlList.begin(); it != xmlList.end();) {
if (it->wait_for(std::chrono::seconds(1)) == std::future_status::ready) {
finished.splice(finished.end(), xmlList, it++);
} else {
JAMI_WARN("PUPnP: XML download timed out");
++it;
}
}
lk.lock();
// Move back failed items to end of list
dwnldlXmlList_.splice(dwnldlXmlList_.end(), xmlList);
// Handle successful downloads
for (auto& item : finished) {
auto result = item.get();
if (not result.document or not validateIgd(result)) {
cpDeviceList_.erase(result.location);
}
}
}
}
UpnpFinish();
});
int upnp_err = UPNP_E_SUCCESS;
char* ip_address = nullptr;
......@@ -109,11 +169,10 @@ PUPnP::PUPnP()
ip_address6 = UpnpGetServerIp6Address();
port6 = UpnpGetServerPort6();
#endif
if (ip_address6 and port6) {
if (ip_address6 and port6)
JAMI_DBG("PUPnP: Initialiazed on %s:%u | %s:%u", ip_address, port, ip_address6, port6);
} else {
else
JAMI_DBG("PUPnP: Initialiazed on %s:%u", ip_address, port);
}
// Relax the parser to allow malformed XML text.
ixmlRelaxParser(1);
......@@ -122,47 +181,133 @@ PUPnP::PUPnP()
PUPnP::~PUPnP()
{
pupnpCv_.notify_all();
// Clear all the lists.
{
std::lock_guard<std::mutex> lk(validIgdMutex_);
for(auto const &it : validIgdList_) {
if (auto igd = dynamic_cast<UPnPIGD*>(it.second.get()))
if (auto igd = std::dynamic_pointer_cast<UPnPIGD>(it.second))
actionDeletePortMappingsByDesc(*igd, Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION);
}
validIgdList_.clear();
cpDeviceList_.clear();
dwnldlXmlList_.clear();
}
// Notify thread to terminate. UpnpFinish function will get called.
pupnpRun_ = false;
pupnpCv_.notify_all();
if (pupnpThread_.joinable()) {
if (pupnpThread_.joinable())
pupnpThread_.join();
}
}
void
PUPnP::clearIGDs()
PUPnP::clearIgds()
{
// Lock internal IGD list.
std::lock_guard<std::mutex> lk(validIgdMutex_);
// Clear internal IGD list.
// Clear all internal lists.
dwnldlXmlList_.clear();
validIgdList_.clear();
cpDeviceList_.clear();
}
void
PUPnP::searchForIGD()
PUPnP::searchForIgd()
{
// Notify registerClientAsync function running in thread to execute in non-blocking fashion.
// Notify thread to execute in non-blocking fashion.
{
std::lock_guard<std::mutex> lk(ctrlptMutex_);
pupnpRun_ = true;
searchForIgd_ = true;
}
pupnpCv_.notify_one();
}
bool
PUPnP::validateIgd(const IGDInfo& info)
{
auto descDoc = info.document.get();
// Check device type.
std::string deviceType = getFirstDocItem(descDoc, "deviceType");
if (deviceType.empty()) {
// No device type.
return false;
}
if (deviceType.compare(UPNP_IGD_DEVICE) != 0) {
// Device type not IGD.
return false;
}
auto igd_candidate = parseIgd(descDoc, info.location);
if (not igd_candidate) {
// No valid IGD candidate.
return false;
}
JAMI_DBG("PUPnP: Validating IGD candidate\n"
" UDN: %s\n"
" Base URL: %s\n"
" Name: %s\n"
" serviceType: %s\n"
" serviceID: %s\n"
" locationURL: %s\n"
" controlURL: %s\n"
" eventSubURL: %s",
igd_candidate->getUDN().c_str(),
igd_candidate->getBaseURL().c_str(),
igd_candidate->getFriendlyName().c_str(),
igd_candidate->getServiceType().c_str(),
igd_candidate->getServiceId().c_str(),
igd_candidate->getLocationURL().c_str(),
igd_candidate->getControlURL().c_str(),
igd_candidate->getEventSubURL().c_str());
// Check if IGD is connected.
if (not actionIsIgdConnected(*igd_candidate)) {
JAMI_WARN("PUPnP: IGD candidate %s is not connected", igd_candidate->getUDN().c_str());
return false;
}
// Validate external Ip.
igd_candidate->publicIp_ = actionGetExternalIP(*igd_candidate);
if (igd_candidate->publicIp_.toString().empty()) {
JAMI_WARN("PUPnP: IGD candidate %s has no valid external Ip", igd_candidate->getUDN().c_str());
return false;
}
// Validate internal Ip.
igd_candidate->localIp_ = ip_utils::getLocalAddr(pj_AF_INET());
if (igd_candidate->localIp_.toString().empty()) {
JAMI_WARN("PUPnP: No valid internal Ip.");
return false;
}
JAMI_DBG("PUPnP: Found device with external IP %s", igd_candidate->publicIp_.toString().c_str());
// Store info for subscription.
std::string eventSub = igd_candidate->getEventSubURL();
std::string udn = igd_candidate->getUDN();
// Remove any local mappings that may be left over from last time used.
removeAllLocalMappings(igd_candidate.get());
// Add the igd to the upnp context class list.
if (updateIgdListCb_(this, std::move(igd_candidate.get()), std::move(igd_candidate.get()->publicIp_), true))
JAMI_DBG("PUPnP: IGD with public IP %s was added to the list", igd_candidate->publicIp_.toString().c_str());
// Keep local IGD list internally.
validIgdList_.emplace(igd_candidate->getUDN(), std::move(igd_candidate)).first;
// Subscribe to IGD events.
int upnp_err = UpnpSubscribeAsync(ctrlptHandle_, eventSub.c_str(), SUBSCRIBE_TIMEOUT, subEventCallback, this);
if (upnp_err != UPNP_E_SUCCESS)
JAMI_WARN("PUPnP: Error when trying to request subscription for %s -> %s", udn.c_str(), UpnpGetErrorMessage(upnp_err));
return true;
}
Mapping
PUPnP::addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, PortType type, UPnPProtocol::UpnpError& upnp_error)
{
......@@ -179,17 +324,17 @@ PUPnP::addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, Port
auto globalMappings = type == PortType::UDP ? &igd->udpMappings : &igd->tcpMappings;
auto iter = globalMappings->find(port_external);
if (iter != globalMappings->end()) {
/* mapping exists with same external port */
// Mapping exists with same external port.
GlobalMapping* mapping_ptr = &iter->second;
if (*mapping_ptr == mapping) {
/* the same mapping, so nothing needs to be done */
// The same mapping, so nothing needs to be done.
upnp_error = UPnPProtocol::UpnpError::ERROR_OK;
++(mapping_ptr->users);
JAMI_DBG("PUPnP: Mapping already exists, incrementing number of users: %d",
iter->second.users);
return mapping;
} else {
/* this port is already used by a different mapping */
// This port is already used by a different mapping.
JAMI_WARN("PUPnP: Cannot add a mapping with an external port which is already used by another:\n\tcurrent: %s\n\ttrying to add: %s",
mapping_ptr->toString().c_str(), mapping.toString().c_str());
upnp_error = UPnPProtocol::UpnpError::CONFLICT_IN_MAPPING;
......@@ -197,7 +342,7 @@ PUPnP::addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, Port
}
}
if (auto pupnp_igd = dynamic_cast<const UPnPIGD*>(igd)) {
if (auto pupnp_igd = dynamic_cast<const UPnPIGD*>(igd)) {
if (actionAddPortMapping(*pupnp_igd, mapping, upnp_error)) {
JAMI_WARN("PUPnP: Opened port %s", mapping.toString().c_str());
globalMappings->emplace(port_external, GlobalMapping{mapping});
......@@ -216,17 +361,15 @@ PUPnP::removeMapping(const Mapping& igdMapping)
// Iterate over all IGDs in internal list and try to remove selected mapping.
for (auto const& item : validIgdList_) {
if (not item.second) {
if (not item.second)
continue;
}
// Get mappings of IGD depending on type.
PortMapGlobal* globalMappings;
if (igdMapping.getType() == PortType::UDP) {
if (igdMapping.getType() == PortType::UDP)
globalMappings = &item.second->udpMappings;
} else {
else
globalMappings = &item.second->tcpMappings;
}
// Check if the mapping we want to remove is present in the IGD.
auto mapToRemove = globalMappings->find(igdMapping.getPortExternal());
......@@ -258,52 +401,16 @@ PUPnP::removeMapping(const Mapping& igdMapping)
void
PUPnP::removeAllLocalMappings(IGD* igd)
{
if (auto igd_del_map = dynamic_cast<UPnPIGD*>(igd)) {
if (auto igd_del_map = dynamic_cast<UPnPIGD*>(igd))
actionDeletePortMappingsByDesc(*igd_del_map, Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION);
}
}
void
PUPnP::registerClientAsync()
{
std::unique_lock<std::mutex> lk(ctrlptMutex_);
while (pupnpRun_) {
pupnpCv_.wait(lk);
if (not clientRegistered_) {
// Register Upnp control point.
int upnp_err = UpnpRegisterClient(ctrlPtCallback, this, &ctrlptHandle_);
if (upnp_err != UPNP_E_SUCCESS) {
JAMI_ERR("PUPnP: Can't register client: %s", UpnpGetErrorMessage(upnp_err));
} else {
clientRegistered_ = true;
}
}
if (not pupnpRun_) {
break;
}
if (clientRegistered_) {
// Send out search for multiple types of devices, as some routers may possibly only reply to one.
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_ROOT_DEVICE, this);
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_IGD_DEVICE, this);
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANIP_SERVICE, this);
UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANPPP_SERVICE, this);
}
}
UpnpFinish();
}
int
PUPnP::ctrlPtCallback(Upnp_EventType event_type, const void* event, void* user_data)
{
if (auto pupnp = static_cast<PUPnP*>(user_data)) {
if (auto pupnp = static_cast<PUPnP*>(user_data))
return pupnp->handleCtrlPtUPnPEvents(event_type, event);
}
JAMI_WARN("PUPnP: Control point callback without PUPnP");
return 0;
}
......@@ -320,122 +427,38 @@ PUPnP::handleCtrlPtUPnPEvents(Upnp_EventType event_type, const void* event)
case UPNP_DISCOVERY_SEARCH_RESULT:
{
const UpnpDiscovery* d_event = (const UpnpDiscovery*)event;
int upnp_err;
// First check the error code.
if (UpnpDiscovery_get_ErrCode(d_event) != UPNP_E_SUCCESS) {
if (UpnpDiscovery_get_ErrCode(d_event) != UPNP_E_SUCCESS)
break;
}
// Check if this device ID is already in the list.
std::string cpDeviceId = UpnpDiscovery_get_DeviceID_cstr(d_event);
std::lock_guard<std::mutex> lk(validIgdMutex_);
if (cpDeviceList_.count(cpDeviceId) > 0) {
if (not cpDeviceList_.emplace(cpDeviceId).second)
break;
}
cpDeviceList_.emplace(std::move(cpDeviceId), std::string{});
/*
* NOTE: This thing will block until success for the system socket timeout
* unless libupnp is compile with '-disable-blocking-tcp-connections', in
* which case it will block for the libupnp specified timeout.
*/
IXML_Document* doc_container_ptr = nullptr;
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> doc_desc_ptr(nullptr, ixmlDocument_free);
upnp_err = UpnpDownloadXmlDoc(UpnpDiscovery_get_Location_cstr(d_event), &doc_container_ptr);
if (doc_container_ptr) {
doc_desc_ptr.reset(doc_container_ptr);
}
if (upnp_err != UPNP_E_SUCCESS or not doc_desc_ptr) {
JAMI_WARN("PUPnP: Error downloading device XML document -> %s", UpnpGetErrorMessage(upnp_err));
break;
}
// Check device type.
std::string deviceType = getFirstDocItem(doc_desc_ptr.get(), "deviceType");
if (deviceType.empty()) {
// No device type. Exit.
break;
}
if (deviceType.compare(UPNP_IGD_DEVICE) != 0) {
// Device type not IGD. Exit.
break;
}
std::unique_ptr<UPnPIGD> igd_candidate;
igd_candidate = parseIGD(doc_desc_ptr.get(), d_event);
if (not igd_candidate) {
// No valid IGD candidate. Exit.
break;
}
JAMI_DBG("PUPnP: Validating IGD candidate.\n\tUDN: %s\n\tBase URL: %s\n\tName: %s\n\tserviceType: %s\n\tserviceID: %s\n\tcontrolURL: %s\n\teventSubURL: %s",
igd_candidate->getUDN().c_str(),
igd_candidate->getBaseURL().c_str(),
igd_candidate->getFriendlyName().c_str(),
igd_candidate->getServiceType().c_str(),
igd_candidate->getServiceId().c_str(),
igd_candidate->getControlURL().c_str(),
igd_candidate->getEventSubURL().c_str());
// Check if IGD is connected.
if (not actionIsIgdConnected(*igd_candidate)) {
JAMI_WARN("PUPnP: IGD candidate %s is not connected", igd_candidate->getUDN().c_str());
break;
}
// Validate external Ip.
igd_candidate->publicIp_ = actionGetExternalIP(*igd_candidate);
if (igd_candidate->publicIp_.toString().empty()) {
JAMI_WARN("PUPnP: IGD candidate %s has no valid external Ip", igd_candidate->getUDN().c_str());
break;
}
// Validate internal Ip.
igd_candidate->localIp_ = ip_utils::getLocalAddr(pj_AF_INET());
if (igd_candidate->localIp_.toString().empty()) {
JAMI_WARN("PUPnP: No valid internal Ip.");
break;
}
JAMI_DBG("PUPnP: Found device with external IP %s", igd_candidate->publicIp_.toString().c_str());
// Store public IP.
std::string publicIpStr(std::move(igd_candidate->publicIp_.toString()));
// Store info for subscription.
std::string eventSub = igd_candidate->getEventSubURL();
std::string udn = igd_candidate->getUDN();
// Remove any local mappings that may be left over from last time used.
removeAllLocalMappings(igd_candidate.get());
// Add the igd to the upnp context class list.
if (updateIgdListCb_(this, std::move(igd_candidate.get()), std::move(igd_candidate.get()->publicIp_), true)) {
JAMI_DBG("PUPnP: IGD with public IP %s was added to the list", publicIpStr.c_str());
} else {
JAMI_DBG("PUPnP: IGD with public IP %s is already in the list", publicIpStr.c_str());
}
// Check if we already downloaded the xml doc based on the igd location string.
std::string igdLocationUrl {UpnpDiscovery_get_Location_cstr(d_event)};
dwnldlXmlList_.emplace_back(dht::ThreadPool::io().get<IGDInfo>([this, location = std::move(igdLocationUrl)]{
IXML_Document* doc_container_ptr = nullptr;
XMLDocument doc_desc_ptr(nullptr, ixmlDocument_free);
int upnp_err = UpnpDownloadXmlDoc(location.c_str(), &doc_container_ptr);
if (doc_container_ptr)
doc_desc_ptr.reset(doc_container_ptr);
pupnpCv_.notify_all();
if (upnp_err != UPNP_E_SUCCESS or not doc_desc_ptr)
JAMI_WARN("PUPnP: Error downloading device XML document -> %s", UpnpGetErrorMessage(upnp_err));
else
return IGDInfo {std::move(location), std::move(doc_desc_ptr)};
return IGDInfo {std::move(location), XMLDocument(nullptr, ixmlDocument_free)};
}));
// Keep local IGD list internally.
if (cpDeviceList_.count(udn) > 0) {
cpDeviceList_[udn] = eventSub;
}
validIgdList_.emplace(std::move(igd_candidate->getUDN()), std::move(igd_candidate));
// Subscribe to IGD events.
upnp_err = UpnpSubscribeAsync(ctrlptHandle_, eventSub.c_str(), SUBSCRIBE_TIMEOUT, subEventCallback, this);
if (upnp_err != UPNP_E_SUCCESS) {
JAMI_WARN("PUPnP: Error when trying to request subscription for %s -> %s", udn.c_str(), UpnpGetErrorMessage(upnp_err));
}
break;
}
case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
{
const UpnpDiscovery *d_event = (const UpnpDiscovery *)event;
std::lock_guard<std::mutex> lk(validIgdMutex_);
// Remvoe device Id from list.
......@@ -444,7 +467,6 @@ PUPnP::handleCtrlPtUPnPEvents(Upnp_EventType event_type, const void* event)
IGD* igd_to_remove = nullptr;
for (auto it = validIgdList_.find(cpDeviceId); it != validIgdList_.end(); it++) {
// Store igd to remove.
igd_to_remove = it->second.get();
......@@ -471,32 +493,17 @@ PUPnP::handleCtrlPtUPnPEvents(Upnp_EventType event_type, const void* event)
case UPNP_EVENT_SUBSCRIPTION_EXPIRED: // This event will occur only if autorenewal is disabled.
{
const UpnpEventSubscribe *es_event = (const UpnpEventSubscribe *)event;
std::string eventSubUrl(UpnpEventSubscribe_get_PublisherUrl_cstr(es_event));
std::pair<std::string, std::string> foundDevice = std::make_pair("", "");
bool foundEventSubUrl = false;
auto it = cpDeviceList_.begin();
while(it != cpDeviceList_.end()) {
if(it->second == eventSubUrl) {
foundEventSubUrl = true;
foundDevice = std::make_pair(it->first, it->second);
break;
}
it++;
}
if (not foundEventSubUrl) {
// If we don't find event subscription url then exit.
break;
auto it = validIgdList_.begin();
for (; it != validIgdList_.end(); it++) {
if (auto igd = std::dynamic_pointer_cast<UPnPIGD>(it->second))
if (igd->getEventSubURL() == eventSubUrl) {
UpnpSubscribeAsync(ctrlptHandle_, eventSubUrl.c_str(), SUBSCRIBE_TIMEOUT, subEventCallback, this);
break;
}
}
std::string udn = foundDevice.first;
std::string eventSub = foundDevice.second;
// Renew subscriptons to IGD events.
UpnpSubscribeAsync(ctrlptHandle_, eventSub.c_str(), SUBSCRIBE_TIMEOUT, subEventCallback, this);
break;
}
case UPNP_EVENT_SUBSCRIBE_COMPLETE:
......@@ -524,15 +531,14 @@ PUPnP::handleCtrlPtUPnPEvents(Upnp_EventType event_type, const void* event)
int
PUPnP::subEventCallback(Upnp_EventType event_type, const void* event, void* user_data)
{
if (auto pupnp = static_cast<PUPnP*>(user_data)) {
if (auto pupnp = static_cast<PUPnP*>(user_data))
return pupnp->handleSubscriptionUPnPEvent(event_type, event);
}
JAMI_WARN("PUPnP: Subscription callback without service Id string");
return 0;
}
int
PUPnP::handleSubscriptionUPnPEvent(Upnp_EventType event_type, const void* event)
PUPnP::handleSubscriptionUPnPEvent(Upnp_EventType /*event_type */, const void* event)
{
std::lock_guard<std::mutex> lk(ctrlptMutex_);
......@@ -550,9 +556,9 @@ PUPnP::handleSubscriptionUPnPEvent(Upnp_EventType event_type, const void* event)
}
std::unique_ptr<UPnPIGD>
PUPnP::parseIGD(IXML_Document* doc, const UpnpDiscovery* d_event)
PUPnP::parseIgd(IXML_Document* doc, std::string locationUrl)
{
if (not doc or not d_event)
if (not (doc and locationUrl.c_str()))
return nullptr;
// Check the UDN to see if its already in our device list.
......@@ -576,9 +582,8 @@ PUPnP::parseIGD(IXML_Document* doc, const UpnpDiscovery* d_event)
// Get base URL.
std::string baseURL = getFirstDocItem(doc, "URLBase");
if (baseURL.empty()) {
baseURL = std::string(UpnpDiscovery_get_Location_cstr(d_event));
}
if (baseURL.empty())
baseURL = locationUrl;
// Get list of services defined by serviceType.
std::unique_ptr<IXML_NodeList, decltype(ixmlNodeList_free)&> serviceList(nullptr, ixmlNodeList_free);
......@@ -627,11 +632,11 @@ PUPnP::parseIGD(IXML_Document* doc, const UpnpDiscovery* d_event)
char* absolute_control_url = nullptr;
upnp_err = UpnpResolveURL2(baseURL.c_str(), controlURL.c_str(), &absolute_control_url);
if (upnp_err == UPNP_E_SUCCESS) {
if (upnp_err == UPNP_E_SUCCESS)
controlURL = absolute_control_url;
} else {
else
JAMI_WARN("PUPnP: Error resolving absolute controlURL -> %s", UpnpGetErrorMessage(upnp_err));
}
std::free(absolute_control_url);
// Get the relative eventSubURL and turn it into absolute address using the URLBase.
......@@ -643,11 +648,11 @@ PUPnP::parseIGD(IXML_Document* doc, const UpnpDiscovery* d_event)
char* absolute_event_sub_url = nullptr;
upnp_err = UpnpResolveURL2(baseURL.c_str(), eventSubURL.c_str(), &absolute_event_sub_url);
if (upnp_err == UPNP_E_SUCCESS) {
if (upnp_err == UPNP_E_SUCCESS)
eventSubURL = absolute_event_sub_url;
} else {
else
JAMI_WARN("PUPnP: Error resolving absolute eventSubURL -> %s", UpnpGetErrorMessage(upnp_err));
}
std::free(absolute_event_sub_url);
new_igd.reset(new UPnPIGD(std::move(UDN),
......@@ -655,6 +660,7 @@ PUPnP::parseIGD(IXML_Document* doc, const UpnpDiscovery* d_event)
std::move(friendlyName),
std::move(serviceType),
std::move(serviceId),
std::move(locationUrl),
std::move(controlURL),
std::move(eventSubURL)));
......@@ -667,9 +673,8 @@ PUPnP::parseIGD(IXML_Document* doc, const UpnpDiscovery* d_event)
bool
PUPnP::actionIsIgdConnected(const UPnPIGD& igd)
{
if (not clientRegistered_) {
if (not clientRegistered_)
return false;
}
// Action and response pointers.
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free); // Action pointer.
......@@ -701,9 +706,8 @@ PUPnP::actionIsIgdConnected(const UPnPIGD& igd)
// Parse response.
std::string status = getFirstDocItem(response.get(), "NewConnectionStatus");
if (status.compare("Connected") != 0) {
if (status.compare("Connected") != 0)
return false;
}
return true;
}
......@@ -711,9 +715,8 @@ PUPnP::actionIsIgdConnected(const UPnPIGD& igd)
IpAddr
PUPnP::actionGetExternalIP(const UPnPIGD& igd)
{
if (not clientRegistered_) {
if (not clientRegistered_)
return {};
}
// Action and response pointers.
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free); // Action pointer.
......@@ -749,13 +752,8 @@ PUPnP::actionGetExternalIP(const UPnPIGD& igd)
void
PUPnP::actionDeletePortMappingsByDesc(const UPnPIGD& igd, const std::string& description)
{
if (not clientRegistered_) {
return;
}
if (!igd.localIp_) {
if (not (clientRegistered_ and igd.localIp_))
return;
}
// Set action name.
std::string action_name { "GetGenericPortMappingEntry" };
......@@ -821,9 +819,8 @@ PUPnP::actionDeletePortMappingsByDesc(const UPnPIGD& igd, const std::string& des
bool
PUPnP::actionDeletePortMapping(const UPnPIGD& igd, const std::string& port_external, const std::string& protocol)
{
if (not clientRegistered_) {
if (not clientRegistered_)
return false;
}
// Action and response pointers.
std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&> action(nullptr, ixmlDocument_free); // Action pointer.
......@@ -869,9 +866,8 @@ PUPnP::actionDeletePortMapping(const UPnPIGD& igd, const std::string& port_exter
bool
PUPnP::actionAddPortMapping(const UPnPIGD& igd, const Mapping& mapping, UPnPProtocol::UpnpError& error_code)
{
if (not clientRegistered_) {
if (not clientRegistered_)
return false;
}
error_code = UPnPProtocol::UpnpError::ERROR_OK;
......
......@@ -2,7 +2,7 @@
* Copyright (C) 2004-2019 Savoir-faire Linux Inc.
*
* Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
* Author: Eden Abitbol <eden.abitbol@savoirfairelinux.com>
* Author: Eden Abitbol <eden.abitbol@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -50,6 +50,12 @@
#include <atomic>
#include <thread>
#include <list>
#include <map>