// // This file is part of the aMule Project. // // Copyright (c) 2004-2011 Marcelo Roberto Jimenez ( phoenix@amule.org ) // Copyright (c) 2006-2011 aMule Team ( admin@amule.org / http://www.amule.org ) // // Any parts of this program derived from the xMule, lMule or eMule project, // or contributed by third-party developers are copyrighted by their // respective authors. // // 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 // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA // #ifdef HAVE_CONFIG_H # include "config.h" // Needed for ENABLE_UPNP #endif #ifdef ENABLE_UPNP #define UPNP_C #include "UPnPBase.h" #include // For dlopen(), dlsym(), dlclose() #include // For transform() #ifdef __GNUC__ #if __GNUC__ >= 4 #define REINTERPRET_CAST(x) reinterpret_cast #endif #endif #ifndef REINTERPRET_CAST // Let's hope that function pointers are equal in size to data pointers #define REINTERPRET_CAST(x) (x) #endif /** * Case insensitive std::string comparison */ bool stdStringIsEqualCI(const std::string &s1, const std::string &s2) { std::string ns1(s1); std::string ns2(s2); std::transform(ns1.begin(), ns1.end(), ns1.begin(), tolower); std::transform(ns2.begin(), ns2.end(), ns2.begin(), tolower); return ns1 == ns2; } CUPnPPortMapping::CUPnPPortMapping( int port, const std::string &protocol, bool enabled, const std::string &description) : m_port(), m_protocol(protocol), m_enabled(enabled ? "1" : "0"), m_description(description), m_key() { std::ostringstream oss; oss << port; m_port = oss.str(); m_key = m_protocol + m_port; } const std::string &CUPnPLib::UPNP_ROOT_DEVICE = "upnp:rootdevice"; const std::string &CUPnPLib::UPNP_DEVICE_IGW = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"; const std::string &CUPnPLib::UPNP_DEVICE_WAN = "urn:schemas-upnp-org:device:WANDevice:1"; const std::string &CUPnPLib::UPNP_DEVICE_WAN_CONNECTION = "urn:schemas-upnp-org:device:WANConnectionDevice:1"; const std::string &CUPnPLib::UPNP_DEVICE_LAN = "urn:schemas-upnp-org:device:LANDevice:1"; const std::string &CUPnPLib::UPNP_SERVICE_LAYER3_FORWARDING = "urn:schemas-upnp-org:service:Layer3Forwarding:1"; const std::string &CUPnPLib::UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG = "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"; const std::string &CUPnPLib::UPNP_SERVICE_WAN_IP_CONNECTION = "urn:schemas-upnp-org:service:WANIPConnection:1"; const std::string &CUPnPLib::UPNP_SERVICE_WAN_PPP_CONNECTION = "urn:schemas-upnp-org:service:WANPPPConnection:1"; CUPnPLib::CUPnPLib(CUPnPControlPoint &ctrlPoint) : m_ctrlPoint(ctrlPoint) { } std::string CUPnPLib::GetUPnPErrorMessage(int code) const { return UpnpGetErrorMessage(code); } std::string CUPnPLib::processUPnPErrorMessage( const std::string &messsage, int errorCode, const DOMString errorString, IXML_Document *doc) const { std::ostringstream msg; if (errorString == NULL || *errorString == 0) { errorString = "Not available"; } if (errorCode > 0) { msg << "Error: " << messsage << ": Error code :'"; if (doc) { CUPnPError e(*this, doc); msg << e.getErrorCode() << "', Error description :'" << e.getErrorDescription() << "'."; } else { msg << errorCode << "', Error description :'" << errorString << "'."; } AddLogLineU(false, logUPnP, msg); } else { msg << "Error: " << messsage << ": UPnP SDK error: " << GetUPnPErrorMessage(errorCode) << " (" << errorCode << ")."; AddLogLineU(false, logUPnP, msg); } return msg.str(); } void CUPnPLib::ProcessActionResponse( IXML_Document *RespDoc, const std::string &actionName) const { std::ostringstream msg; msg << "Response: "; IXML_Element *root = Element_GetRootElement(RespDoc); IXML_Element *child = Element_GetFirstChild(root); if (child) { while (child) { const DOMString childTag = Element_GetTag(child); std::string childValue = Element_GetTextValue(child); msg << "\n " << childTag << "='" << childValue << "'"; child = Element_GetNextSibling(child); } } else { msg << "\n Empty response for action '" << actionName << "'."; } AddDebugLogLineN(logUPnP, msg); } /*! * \brief Returns the root node of a given document. */ IXML_Element *CUPnPLib::Element_GetRootElement( IXML_Document *doc) const { IXML_Element *root = REINTERPRET_CAST(IXML_Element *)( ixmlNode_getFirstChild( REINTERPRET_CAST(IXML_Node *)(doc))); return root; } /*! * \brief Returns the first child of a given element. */ IXML_Element *CUPnPLib::Element_GetFirstChild( IXML_Element *parent) const { IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(parent); IXML_Node *child = ixmlNode_getFirstChild(node); return REINTERPRET_CAST(IXML_Element *)(child); } /*! * \brief Returns the next sibling of a given child. */ IXML_Element *CUPnPLib::Element_GetNextSibling( IXML_Element *child) const { IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(child); IXML_Node *sibling = ixmlNode_getNextSibling(node); return REINTERPRET_CAST(IXML_Element *)(sibling); } /*! * \brief Returns the element tag (name) */ const DOMString CUPnPLib::Element_GetTag( IXML_Element *element) const { IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element); const DOMString tag = ixmlNode_getNodeName(node); return tag; } /*! * \brief Returns the TEXT node value of the current node. */ const std::string CUPnPLib::Element_GetTextValue( IXML_Element *element) const { if (!element) { return stdEmptyString; } IXML_Node *text = ixmlNode_getFirstChild( REINTERPRET_CAST(IXML_Node *)(element)); const DOMString s = ixmlNode_getNodeValue(text); std::string ret; if (s) { ret = s; } return ret; } /*! * \brief Returns the TEXT node value of the first child matching tag. */ const std::string CUPnPLib::Element_GetChildValueByTag( IXML_Element *element, const DOMString tag) const { IXML_Element *child = Element_GetFirstChildByTag(element, tag); return Element_GetTextValue(child); } /*! * \brief Returns the first child element that matches the requested tag or * NULL if not found. */ IXML_Element *CUPnPLib::Element_GetFirstChildByTag( IXML_Element *element, const DOMString tag) const { if (!element || !tag) { return NULL; } IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element); IXML_Node *child = ixmlNode_getFirstChild(node); const DOMString childTag = ixmlNode_getNodeName(child); while(child && childTag && strcmp(tag, childTag)) { child = ixmlNode_getNextSibling(child); childTag = ixmlNode_getNodeName(child); } return REINTERPRET_CAST(IXML_Element *)(child); } /*! * \brief Returns the next sibling element that matches the requested tag. Should be * used with the return value of Element_GetFirstChildByTag(). */ IXML_Element *CUPnPLib::Element_GetNextSiblingByTag( IXML_Element *element, const DOMString tag) const { if (!element || !tag) { return NULL; } IXML_Node *child = REINTERPRET_CAST(IXML_Node *)(element); const DOMString childTag = NULL; do { child = ixmlNode_getNextSibling(child); childTag = ixmlNode_getNodeName(child); } while(child && childTag && strcmp(tag, childTag)); return REINTERPRET_CAST(IXML_Element *)(child); } const std::string CUPnPLib::Element_GetAttributeByTag( IXML_Element *element, const DOMString tag) const { IXML_NamedNodeMap *NamedNodeMap = ixmlNode_getAttributes( REINTERPRET_CAST(IXML_Node *)(element)); IXML_Node *attribute = ixmlNamedNodeMap_getNamedItem(NamedNodeMap, tag); const DOMString s = ixmlNode_getNodeValue(attribute); std::string ret; if (s) { ret = s; } ixmlNamedNodeMap_free(NamedNodeMap); return ret; } CUPnPError::CUPnPError( const CUPnPLib &upnpLib, IXML_Document *errorDoc) : m_root (upnpLib.Element_GetRootElement(errorDoc)), m_ErrorCode (upnpLib.Element_GetChildValueByTag(m_root, "errorCode")), m_ErrorDescription(upnpLib.Element_GetChildValueByTag(m_root, "errorDescription")) { } CUPnPArgument::CUPnPArgument( const CUPnPControlPoint &upnpControlPoint, CUPnPLib &upnpLib, IXML_Element *argument, const std::string &WXUNUSED(SCPDURL)) : m_UPnPControlPoint(upnpControlPoint), m_name (upnpLib.Element_GetChildValueByTag(argument, "name")), m_direction (upnpLib.Element_GetChildValueByTag(argument, "direction")), m_retval (upnpLib.Element_GetFirstChildByTag(argument, "retval")), m_relatedStateVariable(upnpLib.Element_GetChildValueByTag(argument, "relatedStateVariable")) { std::ostringstream msg; msg << "\n Argument:" << "\n name: " << m_name << "\n direction: " << m_direction << "\n retval: " << m_retval << "\n relatedStateVariable: " << m_relatedStateVariable; AddDebugLogLineN(logUPnP, msg); } CUPnPAction::CUPnPAction( const CUPnPControlPoint &upnpControlPoint, CUPnPLib &upnpLib, IXML_Element *action, const std::string &SCPDURL) : m_UPnPControlPoint(upnpControlPoint), m_ArgumentList(upnpControlPoint, upnpLib, action, SCPDURL), m_name(upnpLib.Element_GetChildValueByTag(action, "name")) { std::ostringstream msg; msg << "\n Action:" << "\n name: " << m_name; AddDebugLogLineN(logUPnP, msg); } CUPnPAllowedValue::CUPnPAllowedValue( const CUPnPControlPoint &upnpControlPoint, CUPnPLib &upnpLib, IXML_Element *allowedValue, const std::string &WXUNUSED(SCPDURL)) : m_UPnPControlPoint(upnpControlPoint), m_allowedValue(upnpLib.Element_GetTextValue(allowedValue)) { std::ostringstream msg; msg << "\n AllowedValue:" << "\n allowedValue: " << m_allowedValue; AddDebugLogLineN(logUPnP, msg); } CUPnPStateVariable::CUPnPStateVariable( const CUPnPControlPoint &upnpControlPoint, CUPnPLib &upnpLib, IXML_Element *stateVariable, const std::string &SCPDURL) : m_UPnPControlPoint(upnpControlPoint), m_AllowedValueList(upnpControlPoint, upnpLib, stateVariable, SCPDURL), m_name (upnpLib.Element_GetChildValueByTag(stateVariable, "name")), m_dataType (upnpLib.Element_GetChildValueByTag(stateVariable, "dataType")), m_defaultValue(upnpLib.Element_GetChildValueByTag(stateVariable, "defaultValue")), m_sendEvents (upnpLib.Element_GetAttributeByTag (stateVariable, "sendEvents")) { std::ostringstream msg; msg << "\n StateVariable:" << "\n name: " << m_name << "\n dataType: " << m_dataType << "\n defaultValue: " << m_defaultValue << "\n sendEvents: " << m_sendEvents; AddDebugLogLineN(logUPnP, msg); } CUPnPSCPD::CUPnPSCPD( const CUPnPControlPoint &upnpControlPoint, CUPnPLib &upnpLib, IXML_Element *scpd, const std::string &SCPDURL) : m_UPnPControlPoint(upnpControlPoint), m_ActionList(upnpControlPoint, upnpLib, scpd, SCPDURL), m_ServiceStateTable(upnpControlPoint, upnpLib, scpd, SCPDURL), m_SCPDURL(SCPDURL) { } CUPnPArgumentValue::CUPnPArgumentValue() : m_argument(), m_value() { } CUPnPArgumentValue::CUPnPArgumentValue( const std::string &argument, const std::string &value) : m_argument(argument), m_value(value) { } CUPnPService::CUPnPService( const CUPnPControlPoint &upnpControlPoint, CUPnPLib &upnpLib, IXML_Element *service, const std::string &URLBase) : m_UPnPControlPoint(upnpControlPoint), m_upnpLib(upnpLib), m_serviceType(upnpLib.Element_GetChildValueByTag(service, "serviceType")), m_serviceId (upnpLib.Element_GetChildValueByTag(service, "serviceId")), m_SCPDURL (upnpLib.Element_GetChildValueByTag(service, "SCPDURL")), m_controlURL (upnpLib.Element_GetChildValueByTag(service, "controlURL")), m_eventSubURL(upnpLib.Element_GetChildValueByTag(service, "eventSubURL")), m_timeout(1801), m_SCPD(NULL) { std::ostringstream msg; int errcode; std::vector vscpdURL(URLBase.length() + m_SCPDURL.length() + 1); char *scpdURL = &vscpdURL[0]; errcode = UpnpResolveURL( URLBase.c_str(), m_SCPDURL.c_str(), scpdURL); if( errcode != UPNP_E_SUCCESS ) { msg << "Error generating scpdURL from " << "|" << URLBase << "|" << m_SCPDURL << "|."; AddDebugLogLineN(logUPnP, msg); } else { m_absSCPDURL = scpdURL; } std::vector vcontrolURL( URLBase.length() + m_controlURL.length() + 1); char *controlURL = &vcontrolURL[0]; errcode = UpnpResolveURL( URLBase.c_str(), m_controlURL.c_str(), controlURL); if( errcode != UPNP_E_SUCCESS ) { msg << "Error generating controlURL from " << "|" << URLBase << "|" << m_controlURL << "|."; AddDebugLogLineN(logUPnP, msg); } else { m_absControlURL = controlURL; } std::vector veventURL( URLBase.length() + m_eventSubURL.length() + 1); char *eventURL = &veventURL[0]; errcode = UpnpResolveURL( URLBase.c_str(), m_eventSubURL.c_str(), eventURL); if( errcode != UPNP_E_SUCCESS ) { msg << "Error generating eventURL from " << "|" << URLBase << "|" << m_eventSubURL << "|."; AddDebugLogLineN(logUPnP, msg); } else { m_absEventSubURL = eventURL; } msg << "\n Service:" << "\n serviceType: " << m_serviceType << "\n serviceId: " << m_serviceId << "\n SCPDURL: " << m_SCPDURL << "\n absSCPDURL: " << m_absSCPDURL << "\n controlURL: " << m_controlURL << "\n absControlURL: " << m_absControlURL << "\n eventSubURL: " << m_eventSubURL << "\n absEventSubURL: " << m_absEventSubURL; AddDebugLogLineN(logUPnP, msg); if (m_serviceType == upnpLib.UPNP_SERVICE_WAN_IP_CONNECTION || m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION) { #if 0 m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION || m_serviceType == upnpLib.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG || m_serviceType == upnpLib.UPNP_SERVICE_LAYER3_FORWARDING) { #endif #if 0 //#warning Delete this code on release. if (!upnpLib.m_ctrlPoint.WanServiceDetected()) { // This condition can be used to suspend the parse // of the XML tree. #endif //#warning Delete this code when m_WanService is no longer used. upnpLib.m_ctrlPoint.SetWanService(this); // Log it msg.str(""); msg << "WAN Service Detected: '" << m_serviceType << "'."; AddDebugLogLineC(logUPnP, msg); // Subscribe upnpLib.m_ctrlPoint.Subscribe(*this); #if 0 //#warning Delete this code on release. } else { msg.str(""); msg << "WAN service detected again: '" << m_serviceType << "'. Will only use the first instance."; AddDebugLogLineC(logUPnP, msg); } #endif } else { msg.str(""); msg << "Uninteresting service detected: '" << m_serviceType << "'. Ignoring."; AddDebugLogLineC(logUPnP, msg); } } CUPnPService::~CUPnPService() { } bool CUPnPService::Execute( const std::string &ActionName, const std::vector &ArgValue) const { std::ostringstream msg; if (m_SCPD.get() == NULL) { msg << "Service without SCPD Document, cannot execute action '" << ActionName << "' for service '" << GetServiceType() << "'."; AddDebugLogLineN(logUPnP, msg); return false; } std::ostringstream msgAction("Sending action "); // Check for correct action name ActionList::const_iterator itAction = m_SCPD->GetActionList().find(ActionName); if (itAction == m_SCPD->GetActionList().end()) { msg << "Invalid action name '" << ActionName << "' for service '" << GetServiceType() << "'."; AddDebugLogLineN(logUPnP, msg); return false; } msgAction << ActionName << "("; bool firstTime = true; // Check for correct Argument/Value pairs const CUPnPAction &action = *(itAction->second); for (unsigned int i = 0; i < ArgValue.size(); ++i) { ArgumentList::const_iterator itArg = action.GetArgumentList().find(ArgValue[i].GetArgument()); if (itArg == action.GetArgumentList().end()) { msg << "Invalid argument name '" << ArgValue[i].GetArgument() << "' for action '" << action.GetName() << "' for service '" << GetServiceType() << "'."; AddDebugLogLineN(logUPnP, msg); return false; } const CUPnPArgument &argument = *(itArg->second); if (tolower(argument.GetDirection()[0]) != 'i' || tolower(argument.GetDirection()[1]) != 'n') { msg << "Invalid direction for argument '" << ArgValue[i].GetArgument() << "' for action '" << action.GetName() << "' for service '" << GetServiceType() << "'."; AddDebugLogLineN(logUPnP, msg); return false; } const std::string relatedStateVariableName = argument.GetRelatedStateVariable(); if (!relatedStateVariableName.empty()) { ServiceStateTable::const_iterator itSVT = m_SCPD->GetServiceStateTable(). find(relatedStateVariableName); if (itSVT == m_SCPD->GetServiceStateTable().end()) { msg << "Inconsistent Service State Table, did not find '" << relatedStateVariableName << "' for argument '" << argument.GetName() << "' for action '" << action.GetName() << "' for service '" << GetServiceType() << "'."; AddDebugLogLineN(logUPnP, msg); return false; } const CUPnPStateVariable &stateVariable = *(itSVT->second); if ( !stateVariable.GetAllowedValueList().empty() && stateVariable.GetAllowedValueList().find(ArgValue[i].GetValue()) == stateVariable.GetAllowedValueList().end()) { msg << "Value not allowed '" << ArgValue[i].GetValue() << "' for state variable '" << relatedStateVariableName << "' for argument '" << argument.GetName() << "' for action '" << action.GetName() << "' for service '" << GetServiceType() << "'."; AddDebugLogLineN(logUPnP, msg); return false; } } if (firstTime) { firstTime = false; } else { msgAction << ", "; } msgAction << ArgValue[i].GetArgument() << "='" << ArgValue[i].GetValue() << "'"; } msgAction << ")"; AddDebugLogLineN(logUPnP, msgAction); // Everything is ok, make the action IXML_Document *ActionDoc = NULL; if (ArgValue.size()) { for (unsigned int i = 0; i < ArgValue.size(); ++i) { int ret = UpnpAddToAction( &ActionDoc, action.GetName().c_str(), GetServiceType().c_str(), ArgValue[i].GetArgument().c_str(), ArgValue[i].GetValue().c_str()); if (ret != UPNP_E_SUCCESS) { m_upnpLib.processUPnPErrorMessage( "UpnpAddToAction", ret, NULL, NULL); return false; } } } else { ActionDoc = UpnpMakeAction( action.GetName().c_str(), GetServiceType().c_str(), 0, NULL); if (!ActionDoc) { msg << "Error: UpnpMakeAction returned NULL."; AddLogLineU(false, logUPnP, msg); return false; } } #if 0 // Send the action asynchronously UpnpSendActionAsync( m_UPnPControlPoint.GetUPnPClientHandle(), GetAbsControlURL().c_str(), GetServiceType().c_str(), NULL, ActionDoc, static_cast(&CUPnPControlPoint::Callback), NULL); return true; #endif // Send the action synchronously IXML_Document *RespDoc = NULL; int ret = UpnpSendAction( m_UPnPControlPoint.GetUPnPClientHandle(), GetAbsControlURL().c_str(), GetServiceType().c_str(), NULL, ActionDoc, &RespDoc); if (ret != UPNP_E_SUCCESS) { m_upnpLib.processUPnPErrorMessage( "UpnpSendAction", ret, NULL, RespDoc); ixmlDocument_free(ActionDoc); ixmlDocument_free(RespDoc); return false; } ixmlDocument_free(ActionDoc); // Check the response document m_upnpLib.ProcessActionResponse( RespDoc, action.GetName()); // Free the response document ixmlDocument_free(RespDoc); return true; } const std::string CUPnPService::GetStateVariable( const std::string &stateVariableName) const { std::ostringstream msg; DOMString StVarVal; int ret = UpnpGetServiceVarStatus( m_UPnPControlPoint.GetUPnPClientHandle(), GetAbsControlURL().c_str(), stateVariableName.c_str(), &StVarVal); if (ret != UPNP_E_SUCCESS) { msg << "GetStateVariable(\"" << stateVariableName << "\"): in a call to UpnpGetServiceVarStatus"; m_upnpLib.processUPnPErrorMessage( msg.str(), ret, StVarVal, NULL); return stdEmptyString; } msg << "GetStateVariable: " << stateVariableName << "='" << StVarVal << "'."; AddDebugLogLineN(logUPnP, msg); return stdEmptyString; } CUPnPDevice::CUPnPDevice( const CUPnPControlPoint &upnpControlPoint, CUPnPLib &upnpLib, IXML_Element *device, const std::string &URLBase) : m_UPnPControlPoint(upnpControlPoint), m_DeviceList(upnpControlPoint, upnpLib, device, URLBase), m_ServiceList(upnpControlPoint, upnpLib, device, URLBase), m_deviceType (upnpLib.Element_GetChildValueByTag(device, "deviceType")), m_friendlyName (upnpLib.Element_GetChildValueByTag(device, "friendlyName")), m_manufacturer (upnpLib.Element_GetChildValueByTag(device, "manufacturer")), m_manufacturerURL (upnpLib.Element_GetChildValueByTag(device, "manufacturerURL")), m_modelDescription (upnpLib.Element_GetChildValueByTag(device, "modelDescription")), m_modelName (upnpLib.Element_GetChildValueByTag(device, "modelName")), m_modelNumber (upnpLib.Element_GetChildValueByTag(device, "modelNumber")), m_modelURL (upnpLib.Element_GetChildValueByTag(device, "modelURL")), m_serialNumber (upnpLib.Element_GetChildValueByTag(device, "serialNumber")), m_UDN (upnpLib.Element_GetChildValueByTag(device, "UDN")), m_UPC (upnpLib.Element_GetChildValueByTag(device, "UPC")), m_presentationURL (upnpLib.Element_GetChildValueByTag(device, "presentationURL")) { std::ostringstream msg; int presURLlen = strlen(URLBase.c_str()) + strlen(m_presentationURL.c_str()) + 2; std::vector vpresURL(presURLlen); char* presURL = &vpresURL[0]; int errcode = UpnpResolveURL( URLBase.c_str(), m_presentationURL.c_str(), presURL); if (errcode != UPNP_E_SUCCESS) { msg << "Error generating presentationURL from " << "|" << URLBase << "|" << m_presentationURL << "|."; AddDebugLogLineN(logUPnP, msg); } else { m_presentationURL = presURL; } msg.str(""); msg << "\n Device: " << "\n friendlyName: " << m_friendlyName << "\n deviceType: " << m_deviceType << "\n manufacturer: " << m_manufacturer << "\n manufacturerURL: " << m_manufacturerURL << "\n modelDescription: " << m_modelDescription << "\n modelName: " << m_modelName << "\n modelNumber: " << m_modelNumber << "\n modelURL: " << m_modelURL << "\n serialNumber: " << m_serialNumber << "\n UDN: " << m_UDN << "\n UPC: " << m_UPC << "\n presentationURL: " << m_presentationURL; AddDebugLogLineN(logUPnP, msg); } CUPnPRootDevice::CUPnPRootDevice( const CUPnPControlPoint &upnpControlPoint, CUPnPLib &upnpLib, IXML_Element *rootDevice, const std::string &OriginalURLBase, const std::string &FixedURLBase, const char *location, int expires) : CUPnPDevice(upnpControlPoint, upnpLib, rootDevice, FixedURLBase), m_UPnPControlPoint(upnpControlPoint), m_URLBase(OriginalURLBase), m_location(location), m_expires(expires) { std::ostringstream msg; msg << "\n Root Device: " << "\n URLBase: " << m_URLBase << "\n Fixed URLBase: " << FixedURLBase << "\n location: " << m_location << "\n expires: " << m_expires; AddDebugLogLineN(logUPnP, msg); } CUPnPControlPoint *CUPnPControlPoint::s_CtrlPoint = NULL; CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort) : m_upnpLib(*this), m_UPnPClientHandle(), m_RootDeviceMap(), m_ServiceMap(), m_ActivePortMappingsMap(), m_RootDeviceListMutex(), m_IGWDeviceDetected(false), m_WanService(NULL) { // Pointer to self s_CtrlPoint = this; // Null string at first std::ostringstream msg; // Start UPnP int ret; char *ipAddress = NULL; unsigned short port = 0; ret = UpnpInit(ipAddress, udpPort); if (ret != UPNP_E_SUCCESS) { msg << "error(UpnpInit): Error code "; goto error; } port = UpnpGetServerPort(); ipAddress = UpnpGetServerIpAddress(); msg << "bound to " << ipAddress << ":" << port << "."; AddLogLineU(false, logUPnP, msg); msg.str(""); ret = UpnpRegisterClient( static_cast(&CUPnPControlPoint::Callback), &m_UPnPClientHandle, &m_UPnPClientHandle); if (ret != UPNP_E_SUCCESS) { msg << "error(UpnpRegisterClient): Error registering callback: "; goto error; } // We could ask for just the right device here. If the root device // contains the device we want, it will respond with the full XML doc, // including the root device and every sub-device it has. // // But lets find out what we have in our network by calling UPNP_ROOT_DEVICE. // // We should not search twice, because this will produce two // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems // on the mutex. ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_ROOT_DEVICE.c_str(), NULL); //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this); //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this); //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this); if (ret != UPNP_E_SUCCESS) { msg << "error(UpnpSearchAsync): Error sending search request: "; goto error; } // Wait for the UPnP initialization to complete. { // Lock the search timeout mutex m_WaitForSearchTimeoutMutex.Lock(); // Lock it again, so that we block. Unlocking will only happen // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the // callback. CUPnPMutexLocker lock(m_WaitForSearchTimeoutMutex); } return; // Error processing error: UpnpFinish(); msg << ret << ": " << m_upnpLib.GetUPnPErrorMessage(ret) << "."; throw CUPnPException(msg); } CUPnPControlPoint::~CUPnPControlPoint() { for( RootDeviceMap::iterator it = m_RootDeviceMap.begin(); it != m_RootDeviceMap.end(); ++it) { delete it->second; } // Remove all first // RemoveAll(); UpnpUnRegisterClient(m_UPnPClientHandle); UpnpFinish(); } bool CUPnPControlPoint::AddPortMappings( std::vector &upnpPortMapping) { std::ostringstream msg; if (!WanServiceDetected()) { msg << "UPnP Error: " "CUPnPControlPoint::AddPortMapping: " "WAN Service not detected."; AddLogLineU(true, logUPnP, msg); return false; } int n = upnpPortMapping.size(); bool ok = false; // Check the number of port mappings before std::istringstream PortMappingNumberOfEntries( m_WanService->GetStateVariable( "PortMappingNumberOfEntries")); unsigned long oldNumberOfEntries; PortMappingNumberOfEntries >> oldNumberOfEntries; // Add the enabled port mappings for (int i = 0; i < n; ++i) { if (upnpPortMapping[i].getEnabled() == "1") { // Add the mapping to the control point // active mappings list m_ActivePortMappingsMap[upnpPortMapping[i].getKey()] = upnpPortMapping[i]; // Add the port mapping PrivateAddPortMapping(upnpPortMapping[i]); } } // Test some variables, this is deprecated, might not work // with some routers m_WanService->GetStateVariable("ConnectionType"); m_WanService->GetStateVariable("PossibleConnectionTypes"); m_WanService->GetStateVariable("ConnectionStatus"); m_WanService->GetStateVariable("Uptime"); m_WanService->GetStateVariable("LastConnectionError"); m_WanService->GetStateVariable("RSIPAvailable"); m_WanService->GetStateVariable("NATEnabled"); m_WanService->GetStateVariable("ExternalIPAddress"); m_WanService->GetStateVariable("PortMappingNumberOfEntries"); m_WanService->GetStateVariable("PortMappingLeaseDuration"); // Just for testing std::vector argval; argval.resize(0); m_WanService->Execute("GetStatusInfo", argval); #if 0 // These do not work. Their value must be requested for a // specific port mapping. m_WanService->GetStateVariable("PortMappingEnabled"); m_WanService->GetStateVariable("RemoteHost"); m_WanService->GetStateVariable("ExternalPort"); m_WanService->GetStateVariable("InternalPort"); m_WanService->GetStateVariable("PortMappingProtocol"); m_WanService->GetStateVariable("InternalClient"); m_WanService->GetStateVariable("PortMappingDescription"); #endif // Debug only msg.str(""); msg << "CUPnPControlPoint::DeletePortMappings: " "m_ActivePortMappingsMap.size() == " << m_ActivePortMappingsMap.size(); AddDebugLogLineN(logUPnP, msg); // Not very good, must find a better test PortMappingNumberOfEntries.str( m_WanService->GetStateVariable( "PortMappingNumberOfEntries")); unsigned long newNumberOfEntries; PortMappingNumberOfEntries >> newNumberOfEntries; ok = newNumberOfEntries - oldNumberOfEntries == 4; return ok; } void CUPnPControlPoint::RefreshPortMappings() { for ( PortMappingMap::iterator it = m_ActivePortMappingsMap.begin(); it != m_ActivePortMappingsMap.end(); ++it) { PrivateAddPortMapping(it->second); } // For testing m_WanService->GetStateVariable("PortMappingNumberOfEntries"); } bool CUPnPControlPoint::PrivateAddPortMapping( CUPnPPortMapping &upnpPortMapping) { // Get an IP address. The UPnP server one must do. std::string ipAddress(UpnpGetServerIpAddress()); // Start building the action std::string actionName("AddPortMapping"); std::vector argval(8); // Action parameters argval[0].SetArgument("NewRemoteHost"); argval[0].SetValue(""); argval[1].SetArgument("NewExternalPort"); argval[1].SetValue(upnpPortMapping.getPort()); argval[2].SetArgument("NewProtocol"); argval[2].SetValue(upnpPortMapping.getProtocol()); argval[3].SetArgument("NewInternalPort"); argval[3].SetValue(upnpPortMapping.getPort()); argval[4].SetArgument("NewInternalClient"); argval[4].SetValue(ipAddress); argval[5].SetArgument("NewEnabled"); argval[5].SetValue("1"); argval[6].SetArgument("NewPortMappingDescription"); argval[6].SetValue(upnpPortMapping.getDescription()); argval[7].SetArgument("NewLeaseDuration"); argval[7].SetValue("0"); // Execute bool ret = true; for (ServiceMap::iterator it = m_ServiceMap.begin(); it != m_ServiceMap.end(); ++it) { ret &= it->second->Execute(actionName, argval); } return ret; } bool CUPnPControlPoint::DeletePortMappings( std::vector &upnpPortMapping) { std::ostringstream msg; if (!WanServiceDetected()) { msg << "UPnP Error: " "CUPnPControlPoint::DeletePortMapping: " "WAN Service not detected."; AddLogLineU(true, logUPnP, msg); return false; } int n = upnpPortMapping.size(); bool ok = false; // Check the number of port mappings before std::istringstream PortMappingNumberOfEntries( m_WanService->GetStateVariable( "PortMappingNumberOfEntries")); unsigned long oldNumberOfEntries; PortMappingNumberOfEntries >> oldNumberOfEntries; // Delete the enabled port mappings for (int i = 0; i < n; ++i) { if (upnpPortMapping[i].getEnabled() == "1") { // Delete the mapping from the control point // active mappings list PortMappingMap::iterator it = m_ActivePortMappingsMap.find( upnpPortMapping[i].getKey()); if (it != m_ActivePortMappingsMap.end()) { m_ActivePortMappingsMap.erase(it); } else { msg << "UPnP Error: " "CUPnPControlPoint::DeletePortMapping: " "Mapping was not found in the active " "mapping map."; AddLogLineU(true, logUPnP, msg); } // Delete the port mapping PrivateDeletePortMapping(upnpPortMapping[i]); } } // Debug only msg.str(""); msg << "CUPnPControlPoint::DeletePortMappings: " "m_ActivePortMappingsMap.size() == " << m_ActivePortMappingsMap.size(); AddDebugLogLineN(logUPnP, msg); // Not very good, must find a better test PortMappingNumberOfEntries.str( m_WanService->GetStateVariable( "PortMappingNumberOfEntries")); unsigned long newNumberOfEntries; PortMappingNumberOfEntries >> newNumberOfEntries; ok = oldNumberOfEntries - newNumberOfEntries == 4; return ok; } bool CUPnPControlPoint::PrivateDeletePortMapping( CUPnPPortMapping &upnpPortMapping) { // Start building the action std::string actionName("DeletePortMapping"); std::vector argval(3); // Action parameters argval[0].SetArgument("NewRemoteHost"); argval[0].SetValue(""); argval[1].SetArgument("NewExternalPort"); argval[1].SetValue(upnpPortMapping.getPort()); argval[2].SetArgument("NewProtocol"); argval[2].SetValue(upnpPortMapping.getProtocol()); // Execute bool ret = true; for (ServiceMap::iterator it = m_ServiceMap.begin(); it != m_ServiceMap.end(); ++it) { ret &= it->second->Execute(actionName, argval); } return ret; } // This function is static int CUPnPControlPoint::Callback(Upnp_EventType EventType, void *Event, void * /*Cookie*/) { std::ostringstream msg; std::ostringstream msg2; // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events // happen with a wrong cookie and... boom! // CUPnPControlPoint *upnpCP = static_cast(Cookie); CUPnPControlPoint *upnpCP = CUPnPControlPoint::s_CtrlPoint; //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie); switch (EventType) { case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n"); msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): "; msg2<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: "; goto upnpDiscovery; case UPNP_DISCOVERY_SEARCH_RESULT: { //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n"); msg << "error(UPNP_DISCOVERY_SEARCH_RESULT): "; msg2<< "UPNP_DISCOVERY_SEARCH_RESULT: "; // UPnP Discovery upnpDiscovery: struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event; IXML_Document *doc = NULL; int ret; if (d_event->ErrCode != UPNP_E_SUCCESS) { msg << upnpCP->m_upnpLib.GetUPnPErrorMessage(d_event->ErrCode) << "."; AddDebugLogLineC(logUPnP, msg); } // Get the XML tree device description in doc ret = UpnpDownloadXmlDoc(d_event->Location, &doc); if (ret != UPNP_E_SUCCESS) { msg << "Error retrieving device description from " << d_event->Location << ": " << upnpCP->m_upnpLib.GetUPnPErrorMessage(ret) << "(" << ret << ")."; AddDebugLogLineC(logUPnP, msg); } else { msg2 << "Retrieving device description from " << d_event->Location << "."; AddDebugLogLineN(logUPnP, msg2); } if (doc) { // Get the root node IXML_Element *root = upnpCP->m_upnpLib.Element_GetRootElement(doc); // Extract the URLBase const std::string urlBase = upnpCP->m_upnpLib. Element_GetChildValueByTag(root, "URLBase"); // Get the root device IXML_Element *rootDevice = upnpCP->m_upnpLib. Element_GetFirstChildByTag(root, "device"); // Extract the deviceType std::string devType(upnpCP->m_upnpLib. Element_GetChildValueByTag(rootDevice, "deviceType")); // Only add device if it is an InternetGatewayDevice if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) { // This condition can be used to auto-detect // the UPnP device we are interested in. // Obs.: Don't block the entry here on this // condition! There may be more than one device, // and the first that enters may not be the one // we are interested in! upnpCP->SetIGWDeviceDetected(true); // Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE, // we don't want to spam our logs. if (EventType != UPNP_DISCOVERY_ADVERTISEMENT_ALIVE) { msg.str("Internet Gateway Device Detected."); AddLogLineU(true, logUPnP, msg); } // Add the root device to our list upnpCP->AddRootDevice(rootDevice, urlBase, d_event->Location, d_event->Expires); } // Free the XML doc tree ixmlDocument_free(doc); } break; } case UPNP_DISCOVERY_SEARCH_TIMEOUT: { //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n"); // Search timeout msg << "UPNP_DISCOVERY_SEARCH_TIMEOUT."; AddDebugLogLineN(logUPnP, msg); // Unlock the search timeout mutex upnpCP->m_WaitForSearchTimeoutMutex.Unlock(); break; } case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: { //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n"); // UPnP Device Removed struct Upnp_Discovery *dab_event = (struct Upnp_Discovery *)Event; if (dab_event->ErrCode != UPNP_E_SUCCESS) { msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " << upnpCP->m_upnpLib.GetUPnPErrorMessage(dab_event->ErrCode) << "."; AddDebugLogLineC(logUPnP, msg); } std::string devType = dab_event->DeviceType; // Check for an InternetGatewayDevice and removes it from the list std::transform(devType.begin(), devType.end(), devType.begin(), tolower); if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) { upnpCP->RemoveRootDevice(dab_event->DeviceId); } break; } case UPNP_EVENT_RECEIVED: { //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n"); // Event reveived struct Upnp_Event *e_event = (struct Upnp_Event *)Event; const std::string Sid = e_event->Sid; // Parses the event upnpCP->OnEventReceived(Sid, e_event->EventKey, e_event->ChangedVariables); break; } case UPNP_EVENT_SUBSCRIBE_COMPLETE: //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n"); msg << "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): "; goto upnpEventRenewalComplete; case UPNP_EVENT_UNSUBSCRIBE_COMPLETE: //fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n"); msg << "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): "; goto upnpEventRenewalComplete; case UPNP_EVENT_RENEWAL_COMPLETE: { //fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n"); msg << "error(UPNP_EVENT_RENEWAL_COMPLETE): "; upnpEventRenewalComplete: struct Upnp_Event_Subscribe *es_event = (struct Upnp_Event_Subscribe *)Event; if (es_event->ErrCode != UPNP_E_SUCCESS) { msg << "Error in Event Subscribe Callback"; upnpCP->m_upnpLib.processUPnPErrorMessage( msg.str(), es_event->ErrCode, NULL, NULL); } else { #if 0 TvCtrlPointHandleSubscribeUpdate( es_event->PublisherUrl, es_event->Sid, es_event->TimeOut ); #endif } break; } case UPNP_EVENT_AUTORENEWAL_FAILED: //fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n"); msg << "error(UPNP_EVENT_AUTORENEWAL_FAILED): "; msg2 << "UPNP_EVENT_AUTORENEWAL_FAILED: "; goto upnpEventSubscriptionExpired; case UPNP_EVENT_SUBSCRIPTION_EXPIRED: { //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n"); msg << "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): "; msg2 << "UPNP_EVENT_SUBSCRIPTION_EXPIRED: "; upnpEventSubscriptionExpired: struct Upnp_Event_Subscribe *es_event = (struct Upnp_Event_Subscribe *)Event; Upnp_SID newSID; int TimeOut = 1801; int ret = UpnpSubscribe( upnpCP->m_UPnPClientHandle, es_event->PublisherUrl, &TimeOut, newSID); if (ret != UPNP_E_SUCCESS) { msg << "Error Subscribing to EventURL"; upnpCP->m_upnpLib.processUPnPErrorMessage( msg.str(), es_event->ErrCode, NULL, NULL); } else { ServiceMap::iterator it = upnpCP->m_ServiceMap.find(es_event->PublisherUrl); if (it != upnpCP->m_ServiceMap.end()) { CUPnPService &service = *(it->second); service.SetTimeout(TimeOut); service.SetSID(newSID); msg2 << "Re-subscribed to EventURL '" << es_event->PublisherUrl << "' with SID == '" << newSID << "'."; AddDebugLogLineC(logUPnP, msg2); // In principle, we should test to see if the // service is the same. But here we only have one // service, so... upnpCP->RefreshPortMappings(); } else { msg << "Error: did not find service " << newSID << " in the service map."; AddDebugLogLineC(logUPnP, msg); } } break; } case UPNP_CONTROL_ACTION_COMPLETE: { //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n"); // This is here if we choose to do this asynchronously struct Upnp_Action_Complete *a_event = (struct Upnp_Action_Complete *)Event; if (a_event->ErrCode != UPNP_E_SUCCESS) { upnpCP->m_upnpLib.processUPnPErrorMessage( "UpnpSendActionAsync", a_event->ErrCode, NULL, a_event->ActionResult); } else { // Check the response document upnpCP->m_upnpLib.ProcessActionResponse( a_event->ActionResult, ""); } /* No need for any processing here, just print out results. * Service state table updates are handled by events. */ break; } case UPNP_CONTROL_GET_VAR_COMPLETE: { //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n"); msg << "error(UPNP_CONTROL_GET_VAR_COMPLETE): "; struct Upnp_State_Var_Complete *sv_event = (struct Upnp_State_Var_Complete *)Event; if (sv_event->ErrCode != UPNP_E_SUCCESS) { msg << "m_UpnpGetServiceVarStatusAsync"; upnpCP->m_upnpLib.processUPnPErrorMessage( msg.str(), sv_event->ErrCode, NULL, NULL); } else { #if 0 // Warning: The use of UpnpGetServiceVarStatus and // UpnpGetServiceVarStatusAsync is deprecated by the // UPnP forum. TvCtrlPointHandleGetVar( sv_event->CtrlUrl, sv_event->StateVarName, sv_event->CurrentVal ); #endif } break; } // ignore these cases, since this is not a device case UPNP_CONTROL_GET_VAR_REQUEST: //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n"); msg << "error(UPNP_CONTROL_GET_VAR_REQUEST): "; goto eventSubscriptionRequest; case UPNP_CONTROL_ACTION_REQUEST: //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n"); msg << "error(UPNP_CONTROL_ACTION_REQUEST): "; goto eventSubscriptionRequest; case UPNP_EVENT_SUBSCRIPTION_REQUEST: //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n"); msg << "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): "; eventSubscriptionRequest: msg << "This is not a UPnP Device, this is a UPnP Control Point, event ignored."; AddDebugLogLineC(logUPnP, msg); break; default: // Humm, this is not good, we forgot to handle something... fprintf(stderr, "Callback: default... Unknown event:'%d', not good.\n", EventType); msg << "error(UPnP::Callback): Event not handled:'" << EventType << "'."; fprintf(stderr, "%s\n", msg.str().c_str()); AddDebugLogLineC(logUPnP, msg); // Better not throw in the callback. Who would catch it? //throw CUPnPException(msg); break; } return 0; } void CUPnPControlPoint::OnEventReceived( const std::string &Sid, int EventKey, IXML_Document *ChangedVariablesDoc) { std::ostringstream msg; msg << "UPNP_EVENT_RECEIVED:" << "\n SID: " << Sid << "\n Key: " << EventKey << "\n Property list:"; IXML_Element *root = m_upnpLib.Element_GetRootElement(ChangedVariablesDoc); IXML_Element *child = m_upnpLib.Element_GetFirstChild(root); if (child) { while (child) { IXML_Element *child2 = m_upnpLib.Element_GetFirstChild(child); const DOMString childTag = m_upnpLib.Element_GetTag(child2); std::string childValue = m_upnpLib.Element_GetTextValue(child2); msg << "\n " << childTag << "='" << childValue << "'"; child = m_upnpLib.Element_GetNextSibling(child); } } else { msg << "\n Empty property list."; } AddDebugLogLineC(logUPnP, msg); // Freeing that doc segfaults. Probably should not be freed. //ixmlDocument_free(ChangedVariablesDoc); } void CUPnPControlPoint::AddRootDevice( IXML_Element *rootDevice, const std::string &urlBase, const char *location, int expires) { // Lock the Root Device List CUPnPMutexLocker lock(m_RootDeviceListMutex); // Root node's URLBase std::string OriginalURLBase(urlBase); std::string FixedURLBase(OriginalURLBase.empty() ? location : OriginalURLBase); // Get the UDN (Unique Device Name) std::string UDN( m_upnpLib.Element_GetChildValueByTag(rootDevice, "UDN")); RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN); bool alreadyAdded = it != m_RootDeviceMap.end(); if (alreadyAdded) { // Just set the expires field it->second->SetExpires(expires); } else { // Add a new root device to the root device list CUPnPRootDevice *upnpRootDevice = new CUPnPRootDevice( *this, m_upnpLib, rootDevice, OriginalURLBase, FixedURLBase, location, expires); m_RootDeviceMap[upnpRootDevice->GetUDN()] = upnpRootDevice; } } void CUPnPControlPoint::RemoveRootDevice(const char *udn) { // Lock the Root Device List CUPnPMutexLocker lock(m_RootDeviceListMutex); // Remove std::string UDN(udn); RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN); if (it != m_RootDeviceMap.end()) { delete it->second; m_RootDeviceMap.erase(UDN); } } void CUPnPControlPoint::Subscribe(CUPnPService &service) { std::ostringstream msg; IXML_Document *scpdDoc = NULL; int errcode = UpnpDownloadXmlDoc( service.GetAbsSCPDURL().c_str(), &scpdDoc); if (errcode == UPNP_E_SUCCESS) { // Get the root node of this service (the SCPD Document) IXML_Element *scpdRoot = m_upnpLib.Element_GetRootElement(scpdDoc); CUPnPSCPD *scpd = new CUPnPSCPD(*this, m_upnpLib, scpdRoot, service.GetAbsSCPDURL()); service.SetSCPD(scpd); m_ServiceMap[service.GetAbsEventSubURL()] = &service; msg << "Successfully retrieved SCPD Document for service " << service.GetServiceType() << ", absEventSubURL: " << service.GetAbsEventSubURL() << "."; AddLogLineU(true, logUPnP, msg); msg.str(""); // Now try to subscribe to this service. If the subscription // is not successfull, we will not be notified about events, // but it may be possible to use the service anyway. errcode = UpnpSubscribe(m_UPnPClientHandle, service.GetAbsEventSubURL().c_str(), service.GetTimeoutAddr(), service.GetSID()); if (errcode == UPNP_E_SUCCESS) { msg << "Successfully subscribed to service " << service.GetServiceType() << ", absEventSubURL: " << service.GetAbsEventSubURL() << "."; AddLogLineU(true, logUPnP, msg); } else { msg << "Error subscribing to service " << service.GetServiceType() << ", absEventSubURL: " << service.GetAbsEventSubURL() << ", error: " << m_upnpLib.GetUPnPErrorMessage(errcode) << "."; goto error; } } else { msg << "Error getting SCPD Document from " << service.GetAbsSCPDURL() << "."; AddLogLineU(true, logUPnP, msg); } return; // Error processing error: AddLogLineU(true, logUPnP, msg); } void CUPnPControlPoint::Unsubscribe(CUPnPService &service) { ServiceMap::iterator it = m_ServiceMap.find(service.GetAbsEventSubURL()); if (it != m_ServiceMap.end()) { m_ServiceMap.erase(it); UpnpUnSubscribe(m_UPnPClientHandle, service.GetSID()); } } #endif /* ENABLE_UPNP */