// // This file is part of the aMule Project. // // Copyright (c) 2004-2009 Marcelo Roberto Jimenez ( phoenix@amule.org ) // Copyright (c) 2006-2009 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 // //This file uses libupnp #define UPNP_C #include "UPnPBase.h" #include #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 ex_port, int in_port, const std::string &protocol, bool enabled, const std::string &description) : m_ex_port(), m_in_port(), m_protocol(protocol), m_enabled(enabled ? "1" : "0"), m_description(description), m_key() { std::ostringstream oss; oss << ex_port; m_ex_port = oss.str(); std::ostringstream oss2; oss2 << in_port; m_in_port = oss2.str(); m_key = m_protocol + m_ex_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; #ifdef UPNP_DEBUG if (errorString == NULL || *errorString == 0) { errorString = "Not available"; } if (errorCode > 0) { std::cerr << "CUPnPLib::processUPnPErrorMessage() Error: " << messsage << ": Error code :'"; if (doc) { CUPnPError e(*this, doc); std::cerr << e.getErrorCode() << "', Error description :'" << e.getErrorDescription() << "'."; } else { std::cerr << errorCode << "', Error description :'" << errorString << "'."; } std::cerr << std::endl; } else { std::cerr << "CUPnPLib::processUPnPErrorMessage() Error: " << messsage << ": UPnP SDK error: " << GetUPnPErrorMessage(errorCode) << " (" << errorCode << ")."; std::cerr << std::endl; } #endif 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); #ifdef UPNP_DEBUG std::cerr << "CUPnPLib::ProcessActionResponse() \n " << childTag << "='" << childValue << "'"; #endif //add the variable to the wanservice property map (m_ctrlPoint.m_WanService->propertyMap)[std::string(childTag)] = std::string(childValue); child = Element_GetNextSibling(child); } } else { #ifdef UPNP_DEBUG std::cerr << "CUPnPLib::ProcessActionResponse() \n Empty response for action '" << actionName << "'."; #endif } #ifdef UPNP_DEBUG std::cerr << std::endl; #endif } /*! * \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 &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")) { #ifdef UPNP_DEBUG std::cerr << "CUPnPArgument::CUPnPArgument() \n Argument:" << "\n name: " << m_name << "\n direction: " << m_direction << "\n retval: " << m_retval << "\n relatedStateVariable: " << m_relatedStateVariable; std::cerr << std::endl; #endif } 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")) { #ifdef UPNP_DEBUG std::cerr << "CUPnPAction::CUPnPAction() \n Action:" << "\n name: " << m_name; std::cerr << std::endl; #endif } CUPnPAllowedValue::CUPnPAllowedValue( const CUPnPControlPoint &upnpControlPoint, CUPnPLib &upnpLib, IXML_Element *allowedValue, const std::string &SCPDURL) : m_UPnPControlPoint(upnpControlPoint), m_allowedValue(upnpLib.Element_GetTextValue(allowedValue)) { #ifdef UPNP_DEBUG std::cerr << "CUPnPAllowedValue::CUPnPAllowedValue() \n AllowedValue:" << "\n allowedValue: " << m_allowedValue; std::cerr << std::endl; #endif } 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")) { #ifdef UPNP_DEBUG std::cerr << "CUPnPStateVariable::CUPnPStateVariable() \n StateVariable:" << "\n name: " << m_name << "\n dataType: " << m_dataType << "\n defaultValue: " << m_defaultValue << "\n sendEvents: " << m_sendEvents; std::cerr << std::endl; #endif } 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 ) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::CUPnPService() Error generating scpdURL from " << "|" << URLBase << "|" << m_SCPDURL << "|."; std::cerr << std::endl; #endif } 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 ) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::CUPnPService() Error generating controlURL from " << "|" << URLBase << "|" << m_controlURL << "|."; std::cerr << std::endl; #endif } 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 ) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::CUPnPService() Error generating eventURL from " << "|" << URLBase << "|" << m_eventSubURL << "|."; std::cerr << std::endl; #endif } else { m_absEventSubURL = eventURL; } #ifdef UPNP_DEBUG std::cerr << "CUPnPService::CUPnPService() \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; std::cerr << std::endl; #endif 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(""); #ifdef UPNP_DEBUG std::cerr << "CUPnPService::CUPnPService() WAN Service Detected: '" << m_serviceType << "'."; std::cerr << std::endl; #endif // Subscribe upnpLib.m_ctrlPoint.Subscribe(*this); #if 0 //#warning Delete this code on release. } else { msg.str(""); #ifdef UPNP_DEBUG std::cerr << "WAN service detected again: '" << m_serviceType << "'. Will only use the first instance."; std::cerr << std::endl; #endif } #endif } else { msg.str(""); #ifdef UPNP_DEBUG std::cerr << "CUPnPService::CUPnPService() Uninteresting service detected: '" << m_serviceType << "'. Ignoring."; std::cerr << std::endl; #endif } } CUPnPService::~CUPnPService() { } bool CUPnPService::Execute( const std::string &ActionName, const std::vector &ArgValue) const { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::Execute() called." << std::endl; #endif if (m_SCPD.get() == NULL) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::Execute() Service without SCPD Document, cannot execute action '" << ActionName << "' for service '" << GetServiceType() << "'."; std::cerr << std::endl; #endif return false; } #ifdef UPNP_DEBUG std::cerr << "CUPnPService::Execute() Sending action " << std::endl; #endif // Check for correct action name ActionList::const_iterator itAction = m_SCPD->GetActionList().find(ActionName); if (itAction == m_SCPD->GetActionList().end()) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::Execute() invalid action name '" << ActionName << "' for service '" << GetServiceType() << "'."; std::cerr << std::endl; #endif return false; } std::cerr << 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()) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::Execute() Invalid argument name '" << ArgValue[i].GetArgument() << "' for action '" << action.GetName() << "' for service '" << GetServiceType() << "'."; std::cerr << std::endl; #endif return false; } const CUPnPArgument &argument = *(itArg->second); if (tolower(argument.GetDirection()[0]) != 'i' || tolower(argument.GetDirection()[1]) != 'n') { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::Execute() Invalid direction for argument '" << ArgValue[i].GetArgument() << "' for action '" << action.GetName() << "' for service '" << GetServiceType() << "'."; std::cerr << std::endl; #endif 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()) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::Execute() Inconsistent Service State Table, did not find '" << relatedStateVariableName << "' for argument '" << argument.GetName() << "' for action '" << action.GetName() << "' for service '" << GetServiceType() << "'."; std::cerr << std::endl; #endif return false; } const CUPnPStateVariable &stateVariable = *(itSVT->second); if ( !stateVariable.GetAllowedValueList().empty() && stateVariable.GetAllowedValueList().find(ArgValue[i].GetValue()) == stateVariable.GetAllowedValueList().end()) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::Execute() Value not allowed '" << ArgValue[i].GetValue() << "' for state variable '" << relatedStateVariableName << "' for argument '" << argument.GetName() << "' for action '" << action.GetName() << "' for service '" << GetServiceType() << "'."; #endif return false; } } if (firstTime) { firstTime = false; } else { #ifdef UPNP_DEBUG std::cerr << ", "; #endif } #ifdef UPNP_DEBUG std::cerr << ArgValue[i].GetArgument() << "='" << ArgValue[i].GetValue() << "'"; #endif } #ifdef UPNP_DEBUG std::cerr << ")"; std::cerr << std::endl; #endif // 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 { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::Execute() UpnpMakeAction" << std::endl; #endif ActionDoc = UpnpMakeAction( action.GetName().c_str(), GetServiceType().c_str(), 0, NULL); if (!ActionDoc) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::Execute() Error: UpnpMakeAction returned NULL."; std::cerr << std::endl; #endif return false; } } // Send the action asynchronously UpnpSendActionAsync( m_UPnPControlPoint.GetUPnPClientHandle(), GetAbsControlURL().c_str(), GetServiceType().c_str(), NULL, ActionDoc, static_cast(&CUPnPControlPoint::Callback), NULL); return true; } const std::string CUPnPService::GetStateVariable( const std::string &stateVariableName) { std::map::iterator it; it = propertyMap.find(stateVariableName); if (it != propertyMap.end()) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::GetStateVariable(" << stateVariableName << ") = "; std::cerr << (*it).second << std::endl; #endif return (*it).second; } else { //property map is not populated with the specified value. //we will try to get it with an event //this getvar is just to make the event happening DOMString StVarVal; UpnpGetServiceVarStatus( m_UPnPControlPoint.GetUPnPClientHandle(), GetAbsControlURL().c_str(), stateVariableName.c_str(), &StVarVal); if (StVarVal != NULL) { std::string varValue = std::string(StVarVal); #ifdef UPNP_DEBUG std::cerr << "CUPnPService::GetStateVariable() varValue returned by UpnpGetServiceVarStatus : " << varValue << std::endl; #endif return varValue; } else { //use event to get state variable #ifdef UPNP_DEBUG std::cerr << "CUPnPService::GetStateVariable() pausing in case of an UPnP event incomming."; #endif time_t begin_time = time(NULL); while (true) { if (time(NULL) - begin_time > 7) { break; } } } //propertyMap should be populated by nom it = propertyMap.find(stateVariableName); if (it != propertyMap.end()) { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::GetStateVariable(" << stateVariableName << ") = "; std::cerr << (*it).second << std::endl; #endif return (*it).second; } else { #ifdef UPNP_DEBUG std::cerr << "CUPnPService::GetStateVariable(" << stateVariableName << ") = "; std::cerr << "Empty String" << std::endl; #endif 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) { #ifdef UPNP_DEBUG std::cerr << "CUPnPDevice::CUPnPDevice() Error generating presentationURL from " << "|" << URLBase << "|" << m_presentationURL << "|."; std::cerr << std::endl; #endif } else { m_presentationURL = presURL; } msg.str(""); #ifdef UPNP_DEBUG std::cerr << "CUPnPDevice::CUPnPDevice() \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 << std::endl; #endif } 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) { #ifdef UPNP_DEBUG std::cerr << "CUPnPRootDevice::CUPnPRootDevice() \n Root Device: " << "\n URLBase: " << m_URLBase << "\n Fixed URLBase: " << FixedURLBase << "\n location: " << m_location << "\n expires: " << m_expires << std::endl; #endif } 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) { #ifdef UPNP_DEBUG std::cerr << "UPnPControlPoint::CUPnPControlPoint() Constructor" << std::endl; #endif // Pointer to self s_CtrlPoint = this; // Start UPnP int ret; char *ipAddress = NULL; unsigned short port = 0; #ifdef UPNP_DEBUG int resLog = UpnpInitLog(); std::cerr << "UPnPControlPoint::CUPnPControlPoint() Init log : " << resLog << std::endl; #endif ret = UpnpInit(ipAddress, udpPort); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint Constructor UpnpInit finished" << std::endl; #endif if (ret != UPNP_E_SUCCESS && ret !=UPNP_E_INIT) { #ifdef UPNP_DEBUG std::cerr << "UPnPControlPoint::CUPnPControlPoint() error(UpnpInit): Error code : "; #endif goto error; } port = UpnpGetServerPort(); ipAddress = UpnpGetServerIpAddress(); #ifdef UPNP_DEBUG std::cerr << "UPnPControlPoint::CUPnPControlPoint() bound to " << ipAddress << ":" << port << "." << std::endl; #endif ret = UpnpRegisterClient( static_cast(&CUPnPControlPoint::Callback), &m_UPnPClientHandle, &m_UPnPClientHandle); if (ret != UPNP_E_SUCCESS) { #ifdef UPNP_DEBUG std::cerr << "UPnPControlPoint::CUPnPControlPoint() error(UpnpRegisterClient): Error registering callback: "; #endif 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. // UpnpSetContentLength(m_UPnPClientHandle, 5000000); // UpnpSetMaxContentLength(5000000); ret = UpnpSearchAsync(m_UPnPClientHandle, 20, m_upnpLib.UPNP_DEVICE_IGW.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) { #ifdef UPNP_DEBUG std::cerr << "UPnPControlPoint::CUPnPControlPoint() error(UpnpSearchAsync): Error sending search request: "; #endif 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. #ifdef UPNP_DEBUG std::cerr << "UPnPControlPoint::CUPnPControlPoint() blocking m_WaitForSearchTimeoutMutex." << std::endl; #endif //RsMutex toto(m_WaitForSearchTimeoutMutex); m_WaitForSearchTimeoutMutex.lock(); #ifdef UPNP_DEBUG std::cerr << "UPnPControlPoint::CUPnPControlPoint() m_WaitForSearchTimeoutMutex blocking finished." << std::endl; #endif } //clean the PortMappingNumberOfEntries as it is erroneus on the first event with the french neufbox if (WanServiceDetected()) { m_WanService->propertyMap.erase("PortMappingNumberOfEntries"); } #ifdef UPNP_DEBUG std::cerr << "UPnPControlPoint::CUPnPControlPoint() CUPnPControlPoint Constructor finished" << std::endl; #endif return; // Error processing error: #ifdef UPNP_DEBUG std::cerr << ret << ": " << m_upnpLib.GetUPnPErrorMessage(ret) << "." << std::endl; #endif UpnpFinish(); #ifdef UPNP_DEBUG std::cerr << "UPnPControlPoint::CUPnPControlPoint() UpnpFinish called within CUPnPControlPoint constructor." << std::endl; #endif return; } char* CUPnPControlPoint::getInternalIpAddress() { char * addr = UpnpGetServerIpAddress(); if (addr == NULL) { addr = "127.0.0.1"; } return addr; } CUPnPControlPoint::~CUPnPControlPoint() { for( RootDeviceMap::iterator it = m_RootDeviceMap.begin(); it != m_RootDeviceMap.end(); ++it) { delete it->second; } // Remove all first // RemoveAll(); UpnpUnRegisterClient(m_UPnPClientHandle); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::~CUPnPControlPoint() UpnpFinish called within CUPnPControlPoint destructor." << std::endl; #endif UpnpFinish(); } bool CUPnPControlPoint::AddPortMappings( std::vector &upnpPortMapping) { if (!WanServiceDetected()) { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::AddPortMappings() UPnP Error: " "CUPnPControlPoint::AddPortMapping: " "WAN Service not detected." << std::endl; #endif return false; } #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::AddPortMappings() called." << std::endl; #endif int n = upnpPortMapping.size(); bool ok = false; // Check the number of port mappings before //have a little break in case we just modified the variable, so we have to wait for an event // std::cerr << "GetStateVariable pausing in case of an UPnP event incomming."; // time_t begin_time = time(NULL); // while (true) { // if (time(NULL) - begin_time > 7) { // break; // } // } std::istringstream OldPortMappingNumberOfEntries( m_WanService->GetStateVariable( "PortMappingNumberOfEntries")); int oldNumberOfEntries; OldPortMappingNumberOfEntries >> 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]); } } // Not very good, must find a better test : check the new number of port entries //have a little break in case we just modified the variable, so we have to wait for an event #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::AddPortMappings() GetStateVariable pausing in case of an UPnP event incomming."; #endif time_t begin_time = time(NULL); while (true) { if (time(NULL) - begin_time > 4) { break; } } std::istringstream NewPortMappingNumberOfEntries( m_WanService->GetStateVariable( "PortMappingNumberOfEntries")); int newNumberOfEntries; NewPortMappingNumberOfEntries >> newNumberOfEntries; #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::AddPortMappings() CUPnPControlPoint::AddPortMappings() newNumberOfEntries - oldNumberOfEntries : " << (newNumberOfEntries - oldNumberOfEntries) << std::endl; #endif ok = newNumberOfEntries - oldNumberOfEntries >= 1; #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::AddPortMappings() finished. Success = " << ok << std::endl; #endif return ok; } std::string CUPnPControlPoint::getExternalAddress() { if (!WanServiceDetected()) { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::getExternalAddress() UPnP Error: " "CUPnPControlPoint::AddPortMapping: " "WAN Service not detected." << std::endl; #endif return ""; } std::string result = m_WanService->GetStateVariable("NewExternalIPAddress"); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::getExternalAddress() m_WanService->GetStateVariable(NewExternalIPAddress) = " << result << std::endl; #endif if (result == "") { PrivateGetExternalIpAdress(); result = m_WanService->GetStateVariable("NewExternalIPAddress"); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::getExternalAddress() m_WanService->GetStateVariable(NewExternalIPAddress) = " << result << std::endl; #endif if (result == "") { result = m_WanService->GetStateVariable("ExternalIPAddress"); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::getExternalAddress() m_WanService->GetStateVariable(ExternalIPAddress) = " << result << std::endl; #endif } } return result; } void CUPnPControlPoint::RefreshPortMappings() { for ( PortMappingMap::iterator it = m_ActivePortMappingsMap.begin(); it != m_ActivePortMappingsMap.end(); ++it) { PrivateAddPortMapping(it->second); } } 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.getExPort()); argval[2].SetArgument("NewProtocol"); argval[2].SetValue(upnpPortMapping.getProtocol()); argval[3].SetArgument("NewInternalPort"); argval[3].SetValue(upnpPortMapping.getInPort()); 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) { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::DeletePortMappings() called." << std::endl; #endif if (!WanServiceDetected()) { #ifdef UPNP_DEBUG std::cerr << "UPnP Error: " "CUPnPControlPoint::DeletePortMapping: " "WAN Service not detected." << std::endl; #endif return false; } int n = upnpPortMapping.size(); // 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 { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::DeletePortMappings() UPnP Error: " "CUPnPControlPoint::DeletePortMapping: " "Mapping was not found in the active " "mapping map." << std::endl; #endif } // Delete the port mapping PrivateDeletePortMapping(upnpPortMapping[i]); } } return true; } 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.getExPort()); 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) { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::PrivateDeletePortMapping() Sending a delete port mapping action." << std::endl; #endif ret &= it->second->Execute(actionName, argval); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::PrivateDeletePortMapping() Delete port mapping action finished." << std::endl; #endif } return ret; } bool CUPnPControlPoint::PrivateGetExternalIpAdress() { // Start building the action std::string actionName("GetExternalIPAddress"); std::vector argval(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; } // 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: #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: "; #endif goto upnpDiscovery; case UPNP_DISCOVERY_SEARCH_RESULT: { #ifdef UPNP_DEBUG std::cerr << "UPNP_DISCOVERY_SEARCH_RESULT: "; #endif // 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) { #ifdef UPNP_DEBUG std::cerr << upnpCP->m_upnpLib.GetUPnPErrorMessage(d_event->ErrCode) << "."; std::cerr << std::endl; #endif } #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() URetrieving device description from " << d_event->Location << "." << std::endl; #endif // Get the XML tree device description in doc ret = UpnpDownloadXmlDoc(d_event->Location, &doc); if (ret != UPNP_E_SUCCESS) { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() UError retrieving device description from " << d_event->Location << ": " << upnpCP->m_upnpLib.GetUPnPErrorMessage(ret) << "."; #endif } else { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() URetrieving device description from " << d_event->Location << "." << std::endl; #endif } 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) { #ifdef UPNP_DEBUG std::cerr << "Internet Gateway Device Detected." << std::endl; #endif //} #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() UGetting root device desc." << std::endl; #endif // Add the root device to our list upnpCP->AddRootDevice(rootDevice, urlBase, d_event->Location, d_event->Expires); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() UFinishing getting root device desc." << std::endl; #endif } // Free the XML doc tree ixmlDocument_free(doc); } break; } case UPNP_DISCOVERY_SEARCH_TIMEOUT: { //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n"); // Search timeout #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() UUPNP_DISCOVERY_SEARCH_TIMEOUT : unlocking mutex." << std::endl; #endif // 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) { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() Uerror(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " << upnpCP->m_upnpLib.GetUPnPErrorMessage(dab_event->ErrCode) << "." << std::endl; #endif } 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: { #ifdef UPNP_DEBUG fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n"); #endif // 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 << "CUPnPControlPoint::Callback() 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 << "CUPnPControlPoint::Callback() 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); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() Re-subscribed to EventURL '" << es_event->PublisherUrl << "' with SID == '" << newSID << "'." << std::endl; #endif // In principle, we should test to see if the // service is the same. But here we only have one // service, so... upnpCP->RefreshPortMappings(); } else { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() Error: did not find service " << newSID << " in the service map." << std::endl; #endif } } 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: { #ifdef UPNP_DEBUG fprintf(stderr, "CUPnPControlPoint::Callback() Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n"); #endif msg << "CUPnPControlPoint::Callback() 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 { //add the variable to the wanservice property map (upnpCP->m_WanService->propertyMap)[std::string(sv_event->StateVarName)] = std::string(sv_event->CurrentVal); } 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"); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() error(UPNP_CONTROL_GET_VAR_REQUEST): "; #endif goto eventSubscriptionRequest; case UPNP_CONTROL_ACTION_REQUEST: //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n"); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() error(UPNP_CONTROL_ACTION_REQUEST): "; #endif goto eventSubscriptionRequest; case UPNP_EVENT_SUBSCRIPTION_REQUEST: //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n"); #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() error(UPNP_EVENT_SUBSCRIPTION_REQUEST): "; #endif eventSubscriptionRequest: #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Callback() This is not a UPnP Device, this is a UPnP Control Point, event ignored." << std::endl; #endif break; default: // Humm, this is not good, we forgot to handle something... #ifdef UPNP_DEBUG fprintf(stderr, "Callback: default... Unknown event:'%d', not good.\n", EventType); std::cerr << "CUPnPControlPoint::Callback() error(UPnP::Callback): Event not handled:'" << EventType << "'." << std::endl; #endif // 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) { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::OnEventReceived() UPNP_EVENT_RECEIVED:" << "\n SID: " << Sid << "\n Key: " << EventKey << std::endl; std::cerr << "CUPnPControlPoint::OnEventReceived() m_WanService->GetServiceId() : " << m_WanService->GetSID() << std::endl; #endif if (m_WanService->GetSID() == Sid) { //let's store the properties if it is an event of the wan device #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::OnEventReceived() \n Property list:"; #endif 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); #ifdef UPNP_DEBUG std::cerr << "\n " << childTag << "='" << childValue << "'"; #endif const std::string cTag(childTag); const std::string cValue(childValue); (m_WanService->propertyMap)[cTag] = cValue; child = m_upnpLib.Element_GetNextSibling(child); } } else { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::OnEventReceived() \n Empty property list."; #endif } #ifdef UPNP_DEBUG std::cerr << std::endl; #endif // 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 RsMutex toto(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 RsMutex toto(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) { 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; #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Subscribe() Successfully retrieved SCPD Document for service " << service.GetServiceType() << ", absEventSubURL: " << service.GetAbsEventSubURL() << "." << std::endl; #endif // 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) { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Subscribe() Successfully subscribed to service " << service.GetServiceType() << ", absEventSubURL: " << service.GetAbsEventSubURL() << "." << std::endl; #endif } else { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Subscribe() Error subscribing to service " << service.GetServiceType() << ", absEventSubURL: " << service.GetAbsEventSubURL() << ", error: " << m_upnpLib.GetUPnPErrorMessage(errcode) << "."; #endif goto error; } } else { #ifdef UPNP_DEBUG std::cerr << "CUPnPControlPoint::Subscribe() Error getting SCPD Document from " << service.GetAbsSCPDURL() << "." << std::endl; #endif } return; error: ; #ifdef UPNP_DEBUG std::cerr << std::endl; #endif } 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()); } } // File_checked_for_headers