1// 2// This file is part of the aMule Project. 3// 4// Copyright (c) 2004-2011 Marcelo Roberto Jimenez ( phoenix@amule.org ) 5// Copyright (c) 2006-2011 aMule Team ( admin@amule.org / http://www.amule.org ) 6// 7// Any parts of this program derived from the xMule, lMule or eMule project, 8// or contributed by third-party developers are copyrighted by their 9// respective authors. 10// 11// This program is free software; you can redistribute it and/or modify 12// it under the terms of the GNU General Public License as published by 13// the Free Software Foundation; either version 2 of the License, or 14// (at your option) any later version. 15// 16// This program is distributed in the hope that it will be useful, 17// but WITHOUT ANY WARRANTY; without even the implied warranty of 18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19// GNU General Public License for more details. 20// 21// You should have received a copy of the GNU General Public License 22// along with this program; if not, write to the Free Software 23// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 24// 25 26#ifdef HAVE_CONFIG_H 27# include "config.h" // Needed for ENABLE_UPNP 28#endif 29 30#ifdef ENABLE_UPNP 31 32#define UPNP_C 33 34#include "UPnPBase.h" 35 36 37#include <dlfcn.h> // For dlopen(), dlsym(), dlclose() 38#include <algorithm> // For transform() 39 40 41#ifdef __GNUC__ 42 #if __GNUC__ >= 4 43 #define REINTERPRET_CAST(x) reinterpret_cast<x> 44 #endif 45#endif 46#ifndef REINTERPRET_CAST 47 // Let's hope that function pointers are equal in size to data pointers 48 #define REINTERPRET_CAST(x) (x) 49#endif 50 51 52/** 53 * Case insensitive std::string comparison 54 */ 55bool stdStringIsEqualCI(const std::string &s1, const std::string &s2) 56{ 57 std::string ns1(s1); 58 std::string ns2(s2); 59 std::transform(ns1.begin(), ns1.end(), ns1.begin(), tolower); 60 std::transform(ns2.begin(), ns2.end(), ns2.begin(), tolower); 61 return ns1 == ns2; 62} 63 64 65CUPnPPortMapping::CUPnPPortMapping( 66 int port, 67 const std::string &protocol, 68 bool enabled, 69 const std::string &description) 70: 71m_port(), 72m_protocol(protocol), 73m_enabled(enabled ? "1" : "0"), 74m_description(description), 75m_key() 76{ 77 std::ostringstream oss; 78 oss << port; 79 m_port = oss.str(); 80 m_key = m_protocol + m_port; 81} 82 83 84const std::string &CUPnPLib::UPNP_ROOT_DEVICE = 85 "upnp:rootdevice"; 86 87const std::string &CUPnPLib::UPNP_DEVICE_IGW = 88 "urn:schemas-upnp-org:device:InternetGatewayDevice:1"; 89const std::string &CUPnPLib::UPNP_DEVICE_WAN = 90 "urn:schemas-upnp-org:device:WANDevice:1"; 91const std::string &CUPnPLib::UPNP_DEVICE_WAN_CONNECTION = 92 "urn:schemas-upnp-org:device:WANConnectionDevice:1"; 93const std::string &CUPnPLib::UPNP_DEVICE_LAN = 94 "urn:schemas-upnp-org:device:LANDevice:1"; 95 96const std::string &CUPnPLib::UPNP_SERVICE_LAYER3_FORWARDING = 97 "urn:schemas-upnp-org:service:Layer3Forwarding:1"; 98const std::string &CUPnPLib::UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG = 99 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"; 100const std::string &CUPnPLib::UPNP_SERVICE_WAN_IP_CONNECTION = 101 "urn:schemas-upnp-org:service:WANIPConnection:1"; 102const std::string &CUPnPLib::UPNP_SERVICE_WAN_PPP_CONNECTION = 103 "urn:schemas-upnp-org:service:WANPPPConnection:1"; 104 105 106CUPnPLib::CUPnPLib(CUPnPControlPoint &ctrlPoint) 107: 108m_ctrlPoint(ctrlPoint) 109{ 110} 111 112 113std::string CUPnPLib::GetUPnPErrorMessage(int code) const 114{ 115 return UpnpGetErrorMessage(code); 116} 117 118 119std::string CUPnPLib::processUPnPErrorMessage( 120 const std::string &messsage, 121 int errorCode, 122 const DOMString errorString, 123 IXML_Document *doc) const 124{ 125 std::ostringstream msg; 126 if (errorString == NULL || *errorString == 0) { 127 errorString = "Not available"; 128 } 129 if (errorCode > 0) { 130 msg << "Error: " << 131 messsage << 132 ": Error code :'"; 133 if (doc) { 134 CUPnPError e(*this, doc); 135 msg << e.getErrorCode() << 136 "', Error description :'" << 137 e.getErrorDescription() << 138 "'."; 139 } else { 140 msg << errorCode << 141 "', Error description :'" << 142 errorString << 143 "'."; 144 } 145 AddLogLineU(false, logUPnP, msg); 146 } else { 147 msg << "Error: " << 148 messsage << 149 ": UPnP SDK error: " << 150 GetUPnPErrorMessage(errorCode) << 151 " (" << errorCode << ")."; 152 AddLogLineU(false, logUPnP, msg); 153 } 154 155 return msg.str(); 156} 157 158 159void CUPnPLib::ProcessActionResponse( 160 IXML_Document *RespDoc, 161 const std::string &actionName) const 162{ 163 std::ostringstream msg; 164 msg << "Response: "; 165 IXML_Element *root = Element_GetRootElement(RespDoc); 166 IXML_Element *child = Element_GetFirstChild(root); 167 if (child) { 168 while (child) { 169 const DOMString childTag = Element_GetTag(child); 170 std::string childValue = Element_GetTextValue(child); 171 msg << "\n " << 172 childTag << "='" << 173 childValue << "'"; 174 child = Element_GetNextSibling(child); 175 } 176 } else { 177 msg << "\n Empty response for action '" << 178 actionName << "'."; 179 } 180 AddDebugLogLineN(logUPnP, msg); 181} 182 183 184/*! 185 * \brief Returns the root node of a given document. 186 */ 187IXML_Element *CUPnPLib::Element_GetRootElement( 188 IXML_Document *doc) const 189{ 190 IXML_Element *root = REINTERPRET_CAST(IXML_Element *)( 191 ixmlNode_getFirstChild( 192 REINTERPRET_CAST(IXML_Node *)(doc))); 193 194 return root; 195} 196 197 198/*! 199 * \brief Returns the first child of a given element. 200 */ 201IXML_Element *CUPnPLib::Element_GetFirstChild( 202 IXML_Element *parent) const 203{ 204 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(parent); 205 IXML_Node *child = ixmlNode_getFirstChild(node); 206 207 return REINTERPRET_CAST(IXML_Element *)(child); 208} 209 210 211/*! 212 * \brief Returns the next sibling of a given child. 213 */ 214IXML_Element *CUPnPLib::Element_GetNextSibling( 215 IXML_Element *child) const 216{ 217 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(child); 218 IXML_Node *sibling = ixmlNode_getNextSibling(node); 219 220 return REINTERPRET_CAST(IXML_Element *)(sibling); 221} 222 223 224/*! 225 * \brief Returns the element tag (name) 226 */ 227const DOMString CUPnPLib::Element_GetTag( 228 IXML_Element *element) const 229{ 230 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element); 231 const DOMString tag = ixmlNode_getNodeName(node); 232 233 return tag; 234} 235 236 237/*! 238 * \brief Returns the TEXT node value of the current node. 239 */ 240const std::string CUPnPLib::Element_GetTextValue( 241 IXML_Element *element) const 242{ 243 if (!element) { 244 return stdEmptyString; 245 } 246 IXML_Node *text = ixmlNode_getFirstChild( 247 REINTERPRET_CAST(IXML_Node *)(element)); 248 const DOMString s = ixmlNode_getNodeValue(text); 249 std::string ret; 250 if (s) { 251 ret = s; 252 } 253 254 return ret; 255} 256 257 258/*! 259 * \brief Returns the TEXT node value of the first child matching tag. 260 */ 261const std::string CUPnPLib::Element_GetChildValueByTag( 262 IXML_Element *element, 263 const DOMString tag) const 264{ 265 IXML_Element *child = 266 Element_GetFirstChildByTag(element, tag); 267 268 return Element_GetTextValue(child); 269} 270 271 272/*! 273 * \brief Returns the first child element that matches the requested tag or 274 * NULL if not found. 275 */ 276IXML_Element *CUPnPLib::Element_GetFirstChildByTag( 277 IXML_Element *element, 278 const DOMString tag) const 279{ 280 if (!element || !tag) { 281 return NULL; 282 } 283 284 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element); 285 IXML_Node *child = ixmlNode_getFirstChild(node); 286 const DOMString childTag = ixmlNode_getNodeName(child); 287 while(child && childTag && strcmp(tag, childTag)) { 288 child = ixmlNode_getNextSibling(child); 289 childTag = ixmlNode_getNodeName(child); 290 } 291 292 return REINTERPRET_CAST(IXML_Element *)(child); 293} 294 295 296/*! 297 * \brief Returns the next sibling element that matches the requested tag. Should be 298 * used with the return value of Element_GetFirstChildByTag(). 299 */ 300IXML_Element *CUPnPLib::Element_GetNextSiblingByTag( 301 IXML_Element *element, const DOMString tag) const 302{ 303 if (!element || !tag) { 304 return NULL; 305 } 306 307 IXML_Node *child = REINTERPRET_CAST(IXML_Node *)(element); 308 const DOMString childTag = NULL; 309 do { 310 child = ixmlNode_getNextSibling(child); 311 childTag = ixmlNode_getNodeName(child); 312 } while(child && childTag && strcmp(tag, childTag)); 313 314 return REINTERPRET_CAST(IXML_Element *)(child); 315} 316 317 318const std::string CUPnPLib::Element_GetAttributeByTag( 319 IXML_Element *element, const DOMString tag) const 320{ 321 IXML_NamedNodeMap *NamedNodeMap = ixmlNode_getAttributes( 322 REINTERPRET_CAST(IXML_Node *)(element)); 323 IXML_Node *attribute = ixmlNamedNodeMap_getNamedItem(NamedNodeMap, tag); 324 const DOMString s = ixmlNode_getNodeValue(attribute); 325 std::string ret; 326 if (s) { 327 ret = s; 328 } 329 ixmlNamedNodeMap_free(NamedNodeMap); 330 331 return ret; 332} 333 334 335CUPnPError::CUPnPError( 336 const CUPnPLib &upnpLib, 337 IXML_Document *errorDoc) 338: 339m_root (upnpLib.Element_GetRootElement(errorDoc)), 340m_ErrorCode (upnpLib.Element_GetChildValueByTag(m_root, "errorCode")), 341m_ErrorDescription(upnpLib.Element_GetChildValueByTag(m_root, "errorDescription")) 342{ 343} 344 345 346CUPnPArgument::CUPnPArgument( 347 const CUPnPControlPoint &upnpControlPoint, 348 CUPnPLib &upnpLib, 349 IXML_Element *argument, 350 const std::string &WXUNUSED(SCPDURL)) 351: 352m_UPnPControlPoint(upnpControlPoint), 353m_name (upnpLib.Element_GetChildValueByTag(argument, "name")), 354m_direction (upnpLib.Element_GetChildValueByTag(argument, "direction")), 355m_retval (upnpLib.Element_GetFirstChildByTag(argument, "retval")), 356m_relatedStateVariable(upnpLib.Element_GetChildValueByTag(argument, "relatedStateVariable")) 357{ 358 std::ostringstream msg; 359 msg << "\n Argument:" << 360 "\n name: " << m_name << 361 "\n direction: " << m_direction << 362 "\n retval: " << m_retval << 363 "\n relatedStateVariable: " << m_relatedStateVariable; 364 AddDebugLogLineN(logUPnP, msg); 365} 366 367 368CUPnPAction::CUPnPAction( 369 const CUPnPControlPoint &upnpControlPoint, 370 CUPnPLib &upnpLib, 371 IXML_Element *action, 372 const std::string &SCPDURL) 373: 374m_UPnPControlPoint(upnpControlPoint), 375m_ArgumentList(upnpControlPoint, upnpLib, action, SCPDURL), 376m_name(upnpLib.Element_GetChildValueByTag(action, "name")) 377{ 378 std::ostringstream msg; 379 msg << "\n Action:" << 380 "\n name: " << m_name; 381 AddDebugLogLineN(logUPnP, msg); 382} 383 384 385CUPnPAllowedValue::CUPnPAllowedValue( 386 const CUPnPControlPoint &upnpControlPoint, 387 CUPnPLib &upnpLib, 388 IXML_Element *allowedValue, 389 const std::string &WXUNUSED(SCPDURL)) 390: 391m_UPnPControlPoint(upnpControlPoint), 392m_allowedValue(upnpLib.Element_GetTextValue(allowedValue)) 393{ 394 std::ostringstream msg; 395 msg << "\n AllowedValue:" << 396 "\n allowedValue: " << m_allowedValue; 397 AddDebugLogLineN(logUPnP, msg); 398} 399 400 401CUPnPStateVariable::CUPnPStateVariable( 402 const CUPnPControlPoint &upnpControlPoint, 403 CUPnPLib &upnpLib, 404 IXML_Element *stateVariable, 405 const std::string &SCPDURL) 406: 407m_UPnPControlPoint(upnpControlPoint), 408m_AllowedValueList(upnpControlPoint, upnpLib, stateVariable, SCPDURL), 409m_name (upnpLib.Element_GetChildValueByTag(stateVariable, "name")), 410m_dataType (upnpLib.Element_GetChildValueByTag(stateVariable, "dataType")), 411m_defaultValue(upnpLib.Element_GetChildValueByTag(stateVariable, "defaultValue")), 412m_sendEvents (upnpLib.Element_GetAttributeByTag (stateVariable, "sendEvents")) 413{ 414 std::ostringstream msg; 415 msg << "\n StateVariable:" << 416 "\n name: " << m_name << 417 "\n dataType: " << m_dataType << 418 "\n defaultValue: " << m_defaultValue << 419 "\n sendEvents: " << m_sendEvents; 420 AddDebugLogLineN(logUPnP, msg); 421} 422 423 424CUPnPSCPD::CUPnPSCPD( 425 const CUPnPControlPoint &upnpControlPoint, 426 CUPnPLib &upnpLib, 427 IXML_Element *scpd, 428 const std::string &SCPDURL) 429: 430m_UPnPControlPoint(upnpControlPoint), 431m_ActionList(upnpControlPoint, upnpLib, scpd, SCPDURL), 432m_ServiceStateTable(upnpControlPoint, upnpLib, scpd, SCPDURL), 433m_SCPDURL(SCPDURL) 434{ 435} 436 437 438CUPnPArgumentValue::CUPnPArgumentValue() 439: 440m_argument(), 441m_value() 442{ 443} 444 445 446CUPnPArgumentValue::CUPnPArgumentValue( 447 const std::string &argument, const std::string &value) 448: 449m_argument(argument), 450m_value(value) 451{ 452} 453 454 455CUPnPService::CUPnPService( 456 const CUPnPControlPoint &upnpControlPoint, 457 CUPnPLib &upnpLib, 458 IXML_Element *service, 459 const std::string &URLBase) 460: 461m_UPnPControlPoint(upnpControlPoint), 462m_upnpLib(upnpLib), 463m_serviceType(upnpLib.Element_GetChildValueByTag(service, "serviceType")), 464m_serviceId (upnpLib.Element_GetChildValueByTag(service, "serviceId")), 465m_SCPDURL (upnpLib.Element_GetChildValueByTag(service, "SCPDURL")), 466m_controlURL (upnpLib.Element_GetChildValueByTag(service, "controlURL")), 467m_eventSubURL(upnpLib.Element_GetChildValueByTag(service, "eventSubURL")), 468m_timeout(1801), 469m_SCPD(NULL) 470{ 471 std::ostringstream msg; 472 int errcode; 473 474 std::vector<char> vscpdURL(URLBase.length() + m_SCPDURL.length() + 1); 475 char *scpdURL = &vscpdURL[0]; 476 errcode = UpnpResolveURL( 477 URLBase.c_str(), 478 m_SCPDURL.c_str(), 479 scpdURL); 480 if( errcode != UPNP_E_SUCCESS ) { 481 msg << "Error generating scpdURL from " << 482 "|" << URLBase << "|" << 483 m_SCPDURL << "|."; 484 AddDebugLogLineN(logUPnP, msg); 485 } else { 486 m_absSCPDURL = scpdURL; 487 } 488 489 std::vector<char> vcontrolURL( 490 URLBase.length() + m_controlURL.length() + 1); 491 char *controlURL = &vcontrolURL[0]; 492 errcode = UpnpResolveURL( 493 URLBase.c_str(), 494 m_controlURL.c_str(), 495 controlURL); 496 if( errcode != UPNP_E_SUCCESS ) { 497 msg << "Error generating controlURL from " << 498 "|" << URLBase << "|" << 499 m_controlURL << "|."; 500 AddDebugLogLineN(logUPnP, msg); 501 } else { 502 m_absControlURL = controlURL; 503 } 504 505 std::vector<char> veventURL( 506 URLBase.length() + m_eventSubURL.length() + 1); 507 char *eventURL = &veventURL[0]; 508 errcode = UpnpResolveURL( 509 URLBase.c_str(), 510 m_eventSubURL.c_str(), 511 eventURL); 512 if( errcode != UPNP_E_SUCCESS ) { 513 msg << "Error generating eventURL from " << 514 "|" << URLBase << "|" << 515 m_eventSubURL << "|."; 516 AddDebugLogLineN(logUPnP, msg); 517 } else { 518 m_absEventSubURL = eventURL; 519 } 520 521 msg << "\n Service:" << 522 "\n serviceType: " << m_serviceType << 523 "\n serviceId: " << m_serviceId << 524 "\n SCPDURL: " << m_SCPDURL << 525 "\n absSCPDURL: " << m_absSCPDURL << 526 "\n controlURL: " << m_controlURL << 527 "\n absControlURL: " << m_absControlURL << 528 "\n eventSubURL: " << m_eventSubURL << 529 "\n absEventSubURL: " << m_absEventSubURL; 530 AddDebugLogLineN(logUPnP, msg); 531 532 if (m_serviceType == upnpLib.UPNP_SERVICE_WAN_IP_CONNECTION || 533 m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION) { 534#if 0 535 m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION || 536 m_serviceType == upnpLib.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG || 537 m_serviceType == upnpLib.UPNP_SERVICE_LAYER3_FORWARDING) { 538#endif 539#if 0 540//#warning Delete this code on release. 541 if (!upnpLib.m_ctrlPoint.WanServiceDetected()) { 542 // This condition can be used to suspend the parse 543 // of the XML tree. 544#endif 545//#warning Delete this code when m_WanService is no longer used. 546 upnpLib.m_ctrlPoint.SetWanService(this); 547 // Log it 548 msg.str(""); 549 msg << "WAN Service Detected: '" << 550 m_serviceType << "'."; 551 AddDebugLogLineC(logUPnP, msg); 552 // Subscribe 553 upnpLib.m_ctrlPoint.Subscribe(*this); 554#if 0 555//#warning Delete this code on release. 556 } else { 557 msg.str(""); 558 msg << "WAN service detected again: '" << 559 m_serviceType << 560 "'. Will only use the first instance."; 561 AddDebugLogLineC(logUPnP, msg); 562 } 563#endif 564 } else { 565 msg.str(""); 566 msg << "Uninteresting service detected: '" << 567 m_serviceType << "'. Ignoring."; 568 AddDebugLogLineC(logUPnP, msg); 569 } 570} 571 572 573CUPnPService::~CUPnPService() 574{ 575} 576 577 578bool CUPnPService::Execute( 579 const std::string &ActionName, 580 const std::vector<CUPnPArgumentValue> &ArgValue) const 581{ 582 std::ostringstream msg; 583 if (m_SCPD.get() == NULL) { 584 msg << "Service without SCPD Document, cannot execute action '" << ActionName << 585 "' for service '" << GetServiceType() << "'."; 586 AddDebugLogLineN(logUPnP, msg); 587 return false; 588 } 589 std::ostringstream msgAction("Sending action "); 590 // Check for correct action name 591 ActionList::const_iterator itAction = 592 m_SCPD->GetActionList().find(ActionName); 593 if (itAction == m_SCPD->GetActionList().end()) { 594 msg << "Invalid action name '" << ActionName << 595 "' for service '" << GetServiceType() << "'."; 596 AddDebugLogLineN(logUPnP, msg); 597 return false; 598 } 599 msgAction << ActionName << "("; 600 bool firstTime = true; 601 // Check for correct Argument/Value pairs 602 const CUPnPAction &action = *(itAction->second); 603 for (unsigned int i = 0; i < ArgValue.size(); ++i) { 604 ArgumentList::const_iterator itArg = 605 action.GetArgumentList().find(ArgValue[i].GetArgument()); 606 if (itArg == action.GetArgumentList().end()) { 607 msg << "Invalid argument name '" << ArgValue[i].GetArgument() << 608 "' for action '" << action.GetName() << 609 "' for service '" << GetServiceType() << "'."; 610 AddDebugLogLineN(logUPnP, msg); 611 return false; 612 } 613 const CUPnPArgument &argument = *(itArg->second); 614 if (tolower(argument.GetDirection()[0]) != 'i' || 615 tolower(argument.GetDirection()[1]) != 'n') { 616 msg << "Invalid direction for argument '" << 617 ArgValue[i].GetArgument() << 618 "' for action '" << action.GetName() << 619 "' for service '" << GetServiceType() << "'."; 620 AddDebugLogLineN(logUPnP, msg); 621 return false; 622 } 623 const std::string relatedStateVariableName = 624 argument.GetRelatedStateVariable(); 625 if (!relatedStateVariableName.empty()) { 626 ServiceStateTable::const_iterator itSVT = 627 m_SCPD->GetServiceStateTable(). 628 find(relatedStateVariableName); 629 if (itSVT == m_SCPD->GetServiceStateTable().end()) { 630 msg << "Inconsistent Service State Table, did not find '" << 631 relatedStateVariableName << 632 "' for argument '" << argument.GetName() << 633 "' for action '" << action.GetName() << 634 "' for service '" << GetServiceType() << "'."; 635 AddDebugLogLineN(logUPnP, msg); 636 return false; 637 } 638 const CUPnPStateVariable &stateVariable = *(itSVT->second); 639 if ( !stateVariable.GetAllowedValueList().empty() && 640 stateVariable.GetAllowedValueList().find(ArgValue[i].GetValue()) == 641 stateVariable.GetAllowedValueList().end()) { 642 msg << "Value not allowed '" << ArgValue[i].GetValue() << 643 "' for state variable '" << relatedStateVariableName << 644 "' for argument '" << argument.GetName() << 645 "' for action '" << action.GetName() << 646 "' for service '" << GetServiceType() << "'."; 647 AddDebugLogLineN(logUPnP, msg); 648 return false; 649 } 650 } 651 if (firstTime) { 652 firstTime = false; 653 } else { 654 msgAction << ", "; 655 } 656 msgAction << 657 ArgValue[i].GetArgument() << 658 "='" << 659 ArgValue[i].GetValue() << 660 "'"; 661 } 662 msgAction << ")"; 663 AddDebugLogLineN(logUPnP, msgAction); 664 // Everything is ok, make the action 665 IXML_Document *ActionDoc = NULL; 666 if (ArgValue.size()) { 667 for (unsigned int i = 0; i < ArgValue.size(); ++i) { 668 int ret = UpnpAddToAction( 669 &ActionDoc, 670 action.GetName().c_str(), 671 GetServiceType().c_str(), 672 ArgValue[i].GetArgument().c_str(), 673 ArgValue[i].GetValue().c_str()); 674 if (ret != UPNP_E_SUCCESS) { 675 m_upnpLib.processUPnPErrorMessage( 676 "UpnpAddToAction", ret, NULL, NULL); 677 return false; 678 } 679 } 680 } else { 681 ActionDoc = UpnpMakeAction( 682 action.GetName().c_str(), 683 GetServiceType().c_str(), 684 0, NULL); 685 if (!ActionDoc) { 686 msg << "Error: UpnpMakeAction returned NULL."; 687 AddLogLineU(false, logUPnP, msg); 688 return false; 689 } 690 } 691#if 0 692 // Send the action asynchronously 693 UpnpSendActionAsync( 694 m_UPnPControlPoint.GetUPnPClientHandle(), 695 GetAbsControlURL().c_str(), 696 GetServiceType().c_str(), 697 NULL, ActionDoc, 698 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback), 699 NULL); 700 return true; 701#endif 702 703 // Send the action synchronously 704 IXML_Document *RespDoc = NULL; 705 int ret = UpnpSendAction( 706 m_UPnPControlPoint.GetUPnPClientHandle(), 707 GetAbsControlURL().c_str(), 708 GetServiceType().c_str(), 709 NULL, ActionDoc, &RespDoc); 710 if (ret != UPNP_E_SUCCESS) { 711 m_upnpLib.processUPnPErrorMessage( 712 "UpnpSendAction", ret, NULL, RespDoc); 713 ixmlDocument_free(ActionDoc); 714 ixmlDocument_free(RespDoc); 715 return false; 716 } 717 ixmlDocument_free(ActionDoc); 718 719 // Check the response document 720 m_upnpLib.ProcessActionResponse( 721 RespDoc, action.GetName()); 722 723 // Free the response document 724 ixmlDocument_free(RespDoc); 725 726 return true; 727} 728 729 730const std::string CUPnPService::GetStateVariable( 731 const std::string &stateVariableName) const 732{ 733 std::ostringstream msg; 734 DOMString StVarVal; 735 int ret = UpnpGetServiceVarStatus( 736 m_UPnPControlPoint.GetUPnPClientHandle(), 737 GetAbsControlURL().c_str(), 738 stateVariableName.c_str(), 739 &StVarVal); 740 if (ret != UPNP_E_SUCCESS) { 741 msg << "GetStateVariable(\"" << 742 stateVariableName << 743 "\"): in a call to UpnpGetServiceVarStatus"; 744 m_upnpLib.processUPnPErrorMessage( 745 msg.str(), ret, StVarVal, NULL); 746 return stdEmptyString; 747 } 748 msg << "GetStateVariable: " << 749 stateVariableName << 750 "='" << 751 StVarVal << 752 "'."; 753 AddDebugLogLineN(logUPnP, msg); 754 return stdEmptyString; 755} 756 757 758CUPnPDevice::CUPnPDevice( 759 const CUPnPControlPoint &upnpControlPoint, 760 CUPnPLib &upnpLib, 761 IXML_Element *device, 762 const std::string &URLBase) 763: 764m_UPnPControlPoint(upnpControlPoint), 765m_DeviceList(upnpControlPoint, upnpLib, device, URLBase), 766m_ServiceList(upnpControlPoint, upnpLib, device, URLBase), 767m_deviceType (upnpLib.Element_GetChildValueByTag(device, "deviceType")), 768m_friendlyName (upnpLib.Element_GetChildValueByTag(device, "friendlyName")), 769m_manufacturer (upnpLib.Element_GetChildValueByTag(device, "manufacturer")), 770m_manufacturerURL (upnpLib.Element_GetChildValueByTag(device, "manufacturerURL")), 771m_modelDescription (upnpLib.Element_GetChildValueByTag(device, "modelDescription")), 772m_modelName (upnpLib.Element_GetChildValueByTag(device, "modelName")), 773m_modelNumber (upnpLib.Element_GetChildValueByTag(device, "modelNumber")), 774m_modelURL (upnpLib.Element_GetChildValueByTag(device, "modelURL")), 775m_serialNumber (upnpLib.Element_GetChildValueByTag(device, "serialNumber")), 776m_UDN (upnpLib.Element_GetChildValueByTag(device, "UDN")), 777m_UPC (upnpLib.Element_GetChildValueByTag(device, "UPC")), 778m_presentationURL (upnpLib.Element_GetChildValueByTag(device, "presentationURL")) 779{ 780 std::ostringstream msg; 781 int presURLlen = strlen(URLBase.c_str()) + 782 strlen(m_presentationURL.c_str()) + 2; 783 std::vector<char> vpresURL(presURLlen); 784 char* presURL = &vpresURL[0]; 785 int errcode = UpnpResolveURL( 786 URLBase.c_str(), 787 m_presentationURL.c_str(), 788 presURL); 789 if (errcode != UPNP_E_SUCCESS) { 790 msg << "Error generating presentationURL from " << 791 "|" << URLBase << "|" << 792 m_presentationURL << "|."; 793 AddDebugLogLineN(logUPnP, msg); 794 } else { 795 m_presentationURL = presURL; 796 } 797 798 msg.str(""); 799 msg << "\n Device: " << 800 "\n friendlyName: " << m_friendlyName << 801 "\n deviceType: " << m_deviceType << 802 "\n manufacturer: " << m_manufacturer << 803 "\n manufacturerURL: " << m_manufacturerURL << 804 "\n modelDescription: " << m_modelDescription << 805 "\n modelName: " << m_modelName << 806 "\n modelNumber: " << m_modelNumber << 807 "\n modelURL: " << m_modelURL << 808 "\n serialNumber: " << m_serialNumber << 809 "\n UDN: " << m_UDN << 810 "\n UPC: " << m_UPC << 811 "\n presentationURL: " << m_presentationURL; 812 AddDebugLogLineN(logUPnP, msg); 813} 814 815 816CUPnPRootDevice::CUPnPRootDevice( 817 const CUPnPControlPoint &upnpControlPoint, 818 CUPnPLib &upnpLib, 819 IXML_Element *rootDevice, 820 const std::string &OriginalURLBase, 821 const std::string &FixedURLBase, 822 const char *location, 823 int expires) 824: 825CUPnPDevice(upnpControlPoint, upnpLib, rootDevice, FixedURLBase), 826m_UPnPControlPoint(upnpControlPoint), 827m_URLBase(OriginalURLBase), 828m_location(location), 829m_expires(expires) 830{ 831 std::ostringstream msg; 832 msg << 833 "\n Root Device: " << 834 "\n URLBase: " << m_URLBase << 835 "\n Fixed URLBase: " << FixedURLBase << 836 "\n location: " << m_location << 837 "\n expires: " << m_expires; 838 AddDebugLogLineN(logUPnP, msg); 839} 840 841 842CUPnPControlPoint *CUPnPControlPoint::s_CtrlPoint = NULL; 843 844 845CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort) 846: 847m_upnpLib(*this), 848m_UPnPClientHandle(), 849m_RootDeviceMap(), 850m_ServiceMap(), 851m_ActivePortMappingsMap(), 852m_RootDeviceListMutex(), 853m_IGWDeviceDetected(false), 854m_WanService(NULL) 855{ 856 // Pointer to self 857 s_CtrlPoint = this; 858 // Null string at first 859 std::ostringstream msg; 860 861 // Start UPnP 862 int ret; 863 char *ipAddress = NULL; 864 unsigned short port = 0; 865 ret = UpnpInit(ipAddress, udpPort); 866 if (ret != UPNP_E_SUCCESS) { 867 msg << "error(UpnpInit): Error code "; 868 goto error; 869 } 870 port = UpnpGetServerPort(); 871 ipAddress = UpnpGetServerIpAddress(); 872 msg << "bound to " << ipAddress << ":" << 873 port << "."; 874 AddLogLineU(false, logUPnP, msg); 875 msg.str(""); 876 ret = UpnpRegisterClient( 877 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback), 878 &m_UPnPClientHandle, 879 &m_UPnPClientHandle); 880 if (ret != UPNP_E_SUCCESS) { 881 msg << "error(UpnpRegisterClient): Error registering callback: "; 882 goto error; 883 } 884 885 // We could ask for just the right device here. If the root device 886 // contains the device we want, it will respond with the full XML doc, 887 // including the root device and every sub-device it has. 888 // 889 // But lets find out what we have in our network by calling UPNP_ROOT_DEVICE. 890 // 891 // We should not search twice, because this will produce two 892 // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems 893 // on the mutex. 894 ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_ROOT_DEVICE.c_str(), NULL); 895 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this); 896 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this); 897 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this); 898 if (ret != UPNP_E_SUCCESS) { 899 msg << "error(UpnpSearchAsync): Error sending search request: "; 900 goto error; 901 } 902 903 // Wait for the UPnP initialization to complete. 904 { 905 // Lock the search timeout mutex 906 m_WaitForSearchTimeoutMutex.Lock(); 907 908 // Lock it again, so that we block. Unlocking will only happen 909 // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the 910 // callback. 911 CUPnPMutexLocker lock(m_WaitForSearchTimeoutMutex); 912 } 913 return; 914 915 // Error processing 916error: 917 UpnpFinish(); 918 msg << ret << ": " << m_upnpLib.GetUPnPErrorMessage(ret) << "."; 919 throw CUPnPException(msg); 920} 921 922 923CUPnPControlPoint::~CUPnPControlPoint() 924{ 925 for( RootDeviceMap::iterator it = m_RootDeviceMap.begin(); 926 it != m_RootDeviceMap.end(); 927 ++it) { 928 delete it->second; 929 } 930 // Remove all first 931 // RemoveAll(); 932 UpnpUnRegisterClient(m_UPnPClientHandle); 933 UpnpFinish(); 934} 935 936 937bool CUPnPControlPoint::AddPortMappings( 938 std::vector<CUPnPPortMapping> &upnpPortMapping) 939{ 940 std::ostringstream msg; 941 if (!WanServiceDetected()) { 942 msg << "UPnP Error: " 943 "CUPnPControlPoint::AddPortMapping: " 944 "WAN Service not detected."; 945 AddLogLineU(true, logUPnP, msg); 946 return false; 947 } 948 949 int n = upnpPortMapping.size(); 950 bool ok = false; 951 952 // Check the number of port mappings before 953 std::istringstream PortMappingNumberOfEntries( 954 m_WanService->GetStateVariable( 955 "PortMappingNumberOfEntries")); 956 unsigned long oldNumberOfEntries; 957 PortMappingNumberOfEntries >> oldNumberOfEntries; 958 959 // Add the enabled port mappings 960 for (int i = 0; i < n; ++i) { 961 if (upnpPortMapping[i].getEnabled() == "1") { 962 // Add the mapping to the control point 963 // active mappings list 964 m_ActivePortMappingsMap[upnpPortMapping[i].getKey()] = 965 upnpPortMapping[i]; 966 967 // Add the port mapping 968 PrivateAddPortMapping(upnpPortMapping[i]); 969 } 970 } 971 972 // Test some variables, this is deprecated, might not work 973 // with some routers 974 m_WanService->GetStateVariable("ConnectionType"); 975 m_WanService->GetStateVariable("PossibleConnectionTypes"); 976 m_WanService->GetStateVariable("ConnectionStatus"); 977 m_WanService->GetStateVariable("Uptime"); 978 m_WanService->GetStateVariable("LastConnectionError"); 979 m_WanService->GetStateVariable("RSIPAvailable"); 980 m_WanService->GetStateVariable("NATEnabled"); 981 m_WanService->GetStateVariable("ExternalIPAddress"); 982 m_WanService->GetStateVariable("PortMappingNumberOfEntries"); 983 m_WanService->GetStateVariable("PortMappingLeaseDuration"); 984 985 // Just for testing 986 std::vector<CUPnPArgumentValue> argval; 987 argval.resize(0); 988 m_WanService->Execute("GetStatusInfo", argval); 989 990#if 0 991 // These do not work. Their value must be requested for a 992 // specific port mapping. 993 m_WanService->GetStateVariable("PortMappingEnabled"); 994 m_WanService->GetStateVariable("RemoteHost"); 995 m_WanService->GetStateVariable("ExternalPort"); 996 m_WanService->GetStateVariable("InternalPort"); 997 m_WanService->GetStateVariable("PortMappingProtocol"); 998 m_WanService->GetStateVariable("InternalClient"); 999 m_WanService->GetStateVariable("PortMappingDescription"); 1000#endif 1001 1002 // Debug only 1003 msg.str(""); 1004 msg << "CUPnPControlPoint::DeletePortMappings: " 1005 "m_ActivePortMappingsMap.size() == " << 1006 m_ActivePortMappingsMap.size(); 1007 AddDebugLogLineN(logUPnP, msg); 1008 1009 // Not very good, must find a better test 1010 PortMappingNumberOfEntries.str( 1011 m_WanService->GetStateVariable( 1012 "PortMappingNumberOfEntries")); 1013 unsigned long newNumberOfEntries; 1014 PortMappingNumberOfEntries >> newNumberOfEntries; 1015 ok = newNumberOfEntries - oldNumberOfEntries == 4; 1016 1017 return ok; 1018} 1019 1020 1021void CUPnPControlPoint::RefreshPortMappings() 1022{ 1023 for ( PortMappingMap::iterator it = m_ActivePortMappingsMap.begin(); 1024 it != m_ActivePortMappingsMap.end(); 1025 ++it) { 1026 PrivateAddPortMapping(it->second); 1027 } 1028 1029 // For testing 1030 m_WanService->GetStateVariable("PortMappingNumberOfEntries"); 1031} 1032 1033 1034bool CUPnPControlPoint::PrivateAddPortMapping( 1035 CUPnPPortMapping &upnpPortMapping) 1036{ 1037 // Get an IP address. The UPnP server one must do. 1038 std::string ipAddress(UpnpGetServerIpAddress()); 1039 1040 // Start building the action 1041 std::string actionName("AddPortMapping"); 1042 std::vector<CUPnPArgumentValue> argval(8); 1043 1044 // Action parameters 1045 argval[0].SetArgument("NewRemoteHost"); 1046 argval[0].SetValue(""); 1047 argval[1].SetArgument("NewExternalPort"); 1048 argval[1].SetValue(upnpPortMapping.getPort()); 1049 argval[2].SetArgument("NewProtocol"); 1050 argval[2].SetValue(upnpPortMapping.getProtocol()); 1051 argval[3].SetArgument("NewInternalPort"); 1052 argval[3].SetValue(upnpPortMapping.getPort()); 1053 argval[4].SetArgument("NewInternalClient"); 1054 argval[4].SetValue(ipAddress); 1055 argval[5].SetArgument("NewEnabled"); 1056 argval[5].SetValue("1"); 1057 argval[6].SetArgument("NewPortMappingDescription"); 1058 argval[6].SetValue(upnpPortMapping.getDescription()); 1059 argval[7].SetArgument("NewLeaseDuration"); 1060 argval[7].SetValue("0"); 1061 1062 // Execute 1063 bool ret = true; 1064 for (ServiceMap::iterator it = m_ServiceMap.begin(); 1065 it != m_ServiceMap.end(); ++it) { 1066 ret &= it->second->Execute(actionName, argval); 1067 } 1068 1069 return ret; 1070} 1071 1072 1073bool CUPnPControlPoint::DeletePortMappings( 1074 std::vector<CUPnPPortMapping> &upnpPortMapping) 1075{ 1076 std::ostringstream msg; 1077 if (!WanServiceDetected()) { 1078 msg << "UPnP Error: " 1079 "CUPnPControlPoint::DeletePortMapping: " 1080 "WAN Service not detected."; 1081 AddLogLineU(true, logUPnP, msg); 1082 return false; 1083 } 1084 1085 int n = upnpPortMapping.size(); 1086 bool ok = false; 1087 1088 // Check the number of port mappings before 1089 std::istringstream PortMappingNumberOfEntries( 1090 m_WanService->GetStateVariable( 1091 "PortMappingNumberOfEntries")); 1092 unsigned long oldNumberOfEntries; 1093 PortMappingNumberOfEntries >> oldNumberOfEntries; 1094 1095 // Delete the enabled port mappings 1096 for (int i = 0; i < n; ++i) { 1097 if (upnpPortMapping[i].getEnabled() == "1") { 1098 // Delete the mapping from the control point 1099 // active mappings list 1100 PortMappingMap::iterator it = 1101 m_ActivePortMappingsMap.find( 1102 upnpPortMapping[i].getKey()); 1103 if (it != m_ActivePortMappingsMap.end()) { 1104 m_ActivePortMappingsMap.erase(it); 1105 } else { 1106 msg << "UPnP Error: " 1107 "CUPnPControlPoint::DeletePortMapping: " 1108 "Mapping was not found in the active " 1109 "mapping map."; 1110 AddLogLineU(true, logUPnP, msg); 1111 } 1112 1113 // Delete the port mapping 1114 PrivateDeletePortMapping(upnpPortMapping[i]); 1115 } 1116 } 1117 1118 // Debug only 1119 msg.str(""); 1120 msg << "CUPnPControlPoint::DeletePortMappings: " 1121 "m_ActivePortMappingsMap.size() == " << 1122 m_ActivePortMappingsMap.size(); 1123 AddDebugLogLineN(logUPnP, msg); 1124 1125 // Not very good, must find a better test 1126 PortMappingNumberOfEntries.str( 1127 m_WanService->GetStateVariable( 1128 "PortMappingNumberOfEntries")); 1129 unsigned long newNumberOfEntries; 1130 PortMappingNumberOfEntries >> newNumberOfEntries; 1131 ok = oldNumberOfEntries - newNumberOfEntries == 4; 1132 1133 return ok; 1134} 1135 1136 1137bool CUPnPControlPoint::PrivateDeletePortMapping( 1138 CUPnPPortMapping &upnpPortMapping) 1139{ 1140 // Start building the action 1141 std::string actionName("DeletePortMapping"); 1142 std::vector<CUPnPArgumentValue> argval(3); 1143 1144 // Action parameters 1145 argval[0].SetArgument("NewRemoteHost"); 1146 argval[0].SetValue(""); 1147 argval[1].SetArgument("NewExternalPort"); 1148 argval[1].SetValue(upnpPortMapping.getPort()); 1149 argval[2].SetArgument("NewProtocol"); 1150 argval[2].SetValue(upnpPortMapping.getProtocol()); 1151 1152 // Execute 1153 bool ret = true; 1154 for (ServiceMap::iterator it = m_ServiceMap.begin(); 1155 it != m_ServiceMap.end(); ++it) { 1156 ret &= it->second->Execute(actionName, argval); 1157 } 1158 1159 return ret; 1160} 1161 1162 1163// This function is static 1164int CUPnPControlPoint::Callback(Upnp_EventType EventType, void *Event, void * /*Cookie*/) 1165{ 1166 std::ostringstream msg; 1167 std::ostringstream msg2; 1168 // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events 1169 // happen with a wrong cookie and... boom! 1170 // CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie); 1171 CUPnPControlPoint *upnpCP = CUPnPControlPoint::s_CtrlPoint; 1172 1173 //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie); 1174 switch (EventType) { 1175 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: 1176 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n"); 1177 msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): "; 1178 msg2<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: "; 1179 goto upnpDiscovery; 1180 case UPNP_DISCOVERY_SEARCH_RESULT: { 1181 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n"); 1182 msg << "error(UPNP_DISCOVERY_SEARCH_RESULT): "; 1183 msg2<< "UPNP_DISCOVERY_SEARCH_RESULT: "; 1184 // UPnP Discovery 1185upnpDiscovery: 1186 struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event; 1187 IXML_Document *doc = NULL; 1188 int ret; 1189 if (d_event->ErrCode != UPNP_E_SUCCESS) { 1190 msg << upnpCP->m_upnpLib.GetUPnPErrorMessage(d_event->ErrCode) << "."; 1191 AddDebugLogLineC(logUPnP, msg); 1192 } 1193 // Get the XML tree device description in doc 1194 ret = UpnpDownloadXmlDoc(d_event->Location, &doc); 1195 if (ret != UPNP_E_SUCCESS) { 1196 msg << "Error retrieving device description from " << 1197 d_event->Location << ": " << 1198 upnpCP->m_upnpLib.GetUPnPErrorMessage(ret) << 1199 "(" << ret << ")."; 1200 AddDebugLogLineC(logUPnP, msg); 1201 } else { 1202 msg2 << "Retrieving device description from " << 1203 d_event->Location << "."; 1204 AddDebugLogLineN(logUPnP, msg2); 1205 } 1206 if (doc) { 1207 // Get the root node 1208 IXML_Element *root = 1209 upnpCP->m_upnpLib.Element_GetRootElement(doc); 1210 // Extract the URLBase 1211 const std::string urlBase = upnpCP->m_upnpLib. 1212 Element_GetChildValueByTag(root, "URLBase"); 1213 // Get the root device 1214 IXML_Element *rootDevice = upnpCP->m_upnpLib. 1215 Element_GetFirstChildByTag(root, "device"); 1216 // Extract the deviceType 1217 std::string devType(upnpCP->m_upnpLib. 1218 Element_GetChildValueByTag(rootDevice, "deviceType")); 1219 // Only add device if it is an InternetGatewayDevice 1220 if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) { 1221 // This condition can be used to auto-detect 1222 // the UPnP device we are interested in. 1223 // Obs.: Don't block the entry here on this 1224 // condition! There may be more than one device, 1225 // and the first that enters may not be the one 1226 // we are interested in! 1227 upnpCP->SetIGWDeviceDetected(true); 1228 // Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE, 1229 // we don't want to spam our logs. 1230 if (EventType != UPNP_DISCOVERY_ADVERTISEMENT_ALIVE) { 1231 msg.str("Internet Gateway Device Detected."); 1232 AddLogLineU(true, logUPnP, msg); 1233 } 1234 // Add the root device to our list 1235 upnpCP->AddRootDevice(rootDevice, urlBase, 1236 d_event->Location, d_event->Expires); 1237 } 1238 // Free the XML doc tree 1239 ixmlDocument_free(doc); 1240 } 1241 break; 1242 } 1243 case UPNP_DISCOVERY_SEARCH_TIMEOUT: { 1244 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n"); 1245 // Search timeout 1246 msg << "UPNP_DISCOVERY_SEARCH_TIMEOUT."; 1247 AddDebugLogLineN(logUPnP, msg); 1248 1249 // Unlock the search timeout mutex 1250 upnpCP->m_WaitForSearchTimeoutMutex.Unlock(); 1251 1252 break; 1253 } 1254 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: { 1255 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n"); 1256 // UPnP Device Removed 1257 struct Upnp_Discovery *dab_event = (struct Upnp_Discovery *)Event; 1258 if (dab_event->ErrCode != UPNP_E_SUCCESS) { 1259 msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " << 1260 upnpCP->m_upnpLib.GetUPnPErrorMessage(dab_event->ErrCode) << 1261 "."; 1262 AddDebugLogLineC(logUPnP, msg); 1263 } 1264 std::string devType = dab_event->DeviceType; 1265 // Check for an InternetGatewayDevice and removes it from the list 1266 std::transform(devType.begin(), devType.end(), devType.begin(), tolower); 1267 if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) { 1268 upnpCP->RemoveRootDevice(dab_event->DeviceId); 1269 } 1270 break; 1271 } 1272 case UPNP_EVENT_RECEIVED: { 1273 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n"); 1274 // Event reveived 1275 struct Upnp_Event *e_event = (struct Upnp_Event *)Event; 1276 const std::string Sid = e_event->Sid; 1277 // Parses the event 1278 upnpCP->OnEventReceived(Sid, e_event->EventKey, e_event->ChangedVariables); 1279 break; 1280 } 1281 case UPNP_EVENT_SUBSCRIBE_COMPLETE: 1282 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n"); 1283 msg << "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): "; 1284 goto upnpEventRenewalComplete; 1285 case UPNP_EVENT_UNSUBSCRIBE_COMPLETE: 1286 //fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n"); 1287 msg << "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): "; 1288 goto upnpEventRenewalComplete; 1289 case UPNP_EVENT_RENEWAL_COMPLETE: { 1290 //fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n"); 1291 msg << "error(UPNP_EVENT_RENEWAL_COMPLETE): "; 1292upnpEventRenewalComplete: 1293 struct Upnp_Event_Subscribe *es_event = 1294 (struct Upnp_Event_Subscribe *)Event; 1295 if (es_event->ErrCode != UPNP_E_SUCCESS) { 1296 msg << "Error in Event Subscribe Callback"; 1297 upnpCP->m_upnpLib.processUPnPErrorMessage( 1298 msg.str(), es_event->ErrCode, NULL, NULL); 1299 } else { 1300#if 0 1301 TvCtrlPointHandleSubscribeUpdate( 1302 es_event->PublisherUrl, 1303 es_event->Sid, 1304 es_event->TimeOut ); 1305#endif 1306 } 1307 1308 break; 1309 } 1310 1311 case UPNP_EVENT_AUTORENEWAL_FAILED: 1312 //fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n"); 1313 msg << "error(UPNP_EVENT_AUTORENEWAL_FAILED): "; 1314 msg2 << "UPNP_EVENT_AUTORENEWAL_FAILED: "; 1315 goto upnpEventSubscriptionExpired; 1316 case UPNP_EVENT_SUBSCRIPTION_EXPIRED: { 1317 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n"); 1318 msg << "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): "; 1319 msg2 << "UPNP_EVENT_SUBSCRIPTION_EXPIRED: "; 1320upnpEventSubscriptionExpired: 1321 struct Upnp_Event_Subscribe *es_event = 1322 (struct Upnp_Event_Subscribe *)Event; 1323 Upnp_SID newSID; 1324 int TimeOut = 1801; 1325 int ret = UpnpSubscribe( 1326 upnpCP->m_UPnPClientHandle, 1327 es_event->PublisherUrl, 1328 &TimeOut, 1329 newSID); 1330 if (ret != UPNP_E_SUCCESS) { 1331 msg << "Error Subscribing to EventURL"; 1332 upnpCP->m_upnpLib.processUPnPErrorMessage( 1333 msg.str(), es_event->ErrCode, NULL, NULL); 1334 } else { 1335 ServiceMap::iterator it = 1336 upnpCP->m_ServiceMap.find(es_event->PublisherUrl); 1337 if (it != upnpCP->m_ServiceMap.end()) { 1338 CUPnPService &service = *(it->second); 1339 service.SetTimeout(TimeOut); 1340 service.SetSID(newSID); 1341 msg2 << "Re-subscribed to EventURL '" << 1342 es_event->PublisherUrl << 1343 "' with SID == '" << 1344 newSID << "'."; 1345 AddDebugLogLineC(logUPnP, msg2); 1346 // In principle, we should test to see if the 1347 // service is the same. But here we only have one 1348 // service, so... 1349 upnpCP->RefreshPortMappings(); 1350 } else { 1351 msg << "Error: did not find service " << 1352 newSID << " in the service map."; 1353 AddDebugLogLineC(logUPnP, msg); 1354 } 1355 } 1356 break; 1357 } 1358 case UPNP_CONTROL_ACTION_COMPLETE: { 1359 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n"); 1360 // This is here if we choose to do this asynchronously 1361 struct Upnp_Action_Complete *a_event = 1362 (struct Upnp_Action_Complete *)Event; 1363 if (a_event->ErrCode != UPNP_E_SUCCESS) { 1364 upnpCP->m_upnpLib.processUPnPErrorMessage( 1365 "UpnpSendActionAsync", 1366 a_event->ErrCode, NULL, 1367 a_event->ActionResult); 1368 } else { 1369 // Check the response document 1370 upnpCP->m_upnpLib.ProcessActionResponse( 1371 a_event->ActionResult, 1372 "<UpnpSendActionAsync>"); 1373 } 1374 /* No need for any processing here, just print out results. 1375 * Service state table updates are handled by events. 1376 */ 1377 break; 1378 } 1379 case UPNP_CONTROL_GET_VAR_COMPLETE: { 1380 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n"); 1381 msg << "error(UPNP_CONTROL_GET_VAR_COMPLETE): "; 1382 struct Upnp_State_Var_Complete *sv_event = 1383 (struct Upnp_State_Var_Complete *)Event; 1384 if (sv_event->ErrCode != UPNP_E_SUCCESS) { 1385 msg << "m_UpnpGetServiceVarStatusAsync"; 1386 upnpCP->m_upnpLib.processUPnPErrorMessage( 1387 msg.str(), sv_event->ErrCode, NULL, NULL); 1388 } else { 1389#if 0 1390 // Warning: The use of UpnpGetServiceVarStatus and 1391 // UpnpGetServiceVarStatusAsync is deprecated by the 1392 // UPnP forum. 1393 TvCtrlPointHandleGetVar( 1394 sv_event->CtrlUrl, 1395 sv_event->StateVarName, 1396 sv_event->CurrentVal ); 1397#endif 1398 } 1399 break; 1400 } 1401 // ignore these cases, since this is not a device 1402 case UPNP_CONTROL_GET_VAR_REQUEST: 1403 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n"); 1404 msg << "error(UPNP_CONTROL_GET_VAR_REQUEST): "; 1405 goto eventSubscriptionRequest; 1406 case UPNP_CONTROL_ACTION_REQUEST: 1407 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n"); 1408 msg << "error(UPNP_CONTROL_ACTION_REQUEST): "; 1409 goto eventSubscriptionRequest; 1410 case UPNP_EVENT_SUBSCRIPTION_REQUEST: 1411 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n"); 1412 msg << "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): "; 1413eventSubscriptionRequest: 1414 msg << "This is not a UPnP Device, this is a UPnP Control Point, event ignored."; 1415 AddDebugLogLineC(logUPnP, msg); 1416 break; 1417 default: 1418 // Humm, this is not good, we forgot to handle something... 1419 fprintf(stderr, 1420 "Callback: default... Unknown event:'%d', not good.\n", 1421 EventType); 1422 msg << "error(UPnP::Callback): Event not handled:'" << 1423 EventType << "'."; 1424 fprintf(stderr, "%s\n", msg.str().c_str()); 1425 AddDebugLogLineC(logUPnP, msg); 1426 // Better not throw in the callback. Who would catch it? 1427 //throw CUPnPException(msg); 1428 break; 1429 } 1430 1431 return 0; 1432} 1433 1434 1435void CUPnPControlPoint::OnEventReceived( 1436 const std::string &Sid, 1437 int EventKey, 1438 IXML_Document *ChangedVariablesDoc) 1439{ 1440 std::ostringstream msg; 1441 msg << "UPNP_EVENT_RECEIVED:" << 1442 "\n SID: " << Sid << 1443 "\n Key: " << EventKey << 1444 "\n Property list:"; 1445 IXML_Element *root = 1446 m_upnpLib.Element_GetRootElement(ChangedVariablesDoc); 1447 IXML_Element *child = 1448 m_upnpLib.Element_GetFirstChild(root); 1449 if (child) { 1450 while (child) { 1451 IXML_Element *child2 = 1452 m_upnpLib.Element_GetFirstChild(child); 1453 const DOMString childTag = 1454 m_upnpLib.Element_GetTag(child2); 1455 std::string childValue = 1456 m_upnpLib.Element_GetTextValue(child2); 1457 msg << "\n " << 1458 childTag << "='" << 1459 childValue << "'"; 1460 child = m_upnpLib.Element_GetNextSibling(child); 1461 } 1462 } else { 1463 msg << "\n Empty property list."; 1464 } 1465 AddDebugLogLineC(logUPnP, msg); 1466 // Freeing that doc segfaults. Probably should not be freed. 1467 //ixmlDocument_free(ChangedVariablesDoc); 1468} 1469 1470 1471void CUPnPControlPoint::AddRootDevice( 1472 IXML_Element *rootDevice, const std::string &urlBase, 1473 const char *location, int expires) 1474{ 1475 // Lock the Root Device List 1476 CUPnPMutexLocker lock(m_RootDeviceListMutex); 1477 1478 // Root node's URLBase 1479 std::string OriginalURLBase(urlBase); 1480 std::string FixedURLBase(OriginalURLBase.empty() ? 1481 location : 1482 OriginalURLBase); 1483 1484 // Get the UDN (Unique Device Name) 1485 std::string UDN( 1486 m_upnpLib.Element_GetChildValueByTag(rootDevice, "UDN")); 1487 RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN); 1488 bool alreadyAdded = it != m_RootDeviceMap.end(); 1489 if (alreadyAdded) { 1490 // Just set the expires field 1491 it->second->SetExpires(expires); 1492 } else { 1493 // Add a new root device to the root device list 1494 CUPnPRootDevice *upnpRootDevice = new CUPnPRootDevice( 1495 *this, m_upnpLib, rootDevice, 1496 OriginalURLBase, FixedURLBase, 1497 location, expires); 1498 m_RootDeviceMap[upnpRootDevice->GetUDN()] = upnpRootDevice; 1499 } 1500} 1501 1502 1503void CUPnPControlPoint::RemoveRootDevice(const char *udn) 1504{ 1505 // Lock the Root Device List 1506 CUPnPMutexLocker lock(m_RootDeviceListMutex); 1507 1508 // Remove 1509 std::string UDN(udn); 1510 RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN); 1511 if (it != m_RootDeviceMap.end()) { 1512 delete it->second; 1513 m_RootDeviceMap.erase(UDN); 1514 } 1515} 1516 1517 1518void CUPnPControlPoint::Subscribe(CUPnPService &service) 1519{ 1520 std::ostringstream msg; 1521 1522 IXML_Document *scpdDoc = NULL; 1523 int errcode = UpnpDownloadXmlDoc( 1524 service.GetAbsSCPDURL().c_str(), &scpdDoc); 1525 if (errcode == UPNP_E_SUCCESS) { 1526 // Get the root node of this service (the SCPD Document) 1527 IXML_Element *scpdRoot = 1528 m_upnpLib.Element_GetRootElement(scpdDoc); 1529 CUPnPSCPD *scpd = new CUPnPSCPD(*this, m_upnpLib, 1530 scpdRoot, service.GetAbsSCPDURL()); 1531 service.SetSCPD(scpd); 1532 m_ServiceMap[service.GetAbsEventSubURL()] = &service; 1533 msg << "Successfully retrieved SCPD Document for service " << 1534 service.GetServiceType() << ", absEventSubURL: " << 1535 service.GetAbsEventSubURL() << "."; 1536 AddLogLineU(true, logUPnP, msg); 1537 msg.str(""); 1538 1539 // Now try to subscribe to this service. If the subscription 1540 // is not successfull, we will not be notified about events, 1541 // but it may be possible to use the service anyway. 1542 errcode = UpnpSubscribe(m_UPnPClientHandle, 1543 service.GetAbsEventSubURL().c_str(), 1544 service.GetTimeoutAddr(), 1545 service.GetSID()); 1546 if (errcode == UPNP_E_SUCCESS) { 1547 msg << "Successfully subscribed to service " << 1548 service.GetServiceType() << ", absEventSubURL: " << 1549 service.GetAbsEventSubURL() << "."; 1550 AddLogLineU(true, logUPnP, msg); 1551 } else { 1552 msg << "Error subscribing to service " << 1553 service.GetServiceType() << ", absEventSubURL: " << 1554 service.GetAbsEventSubURL() << ", error: " << 1555 m_upnpLib.GetUPnPErrorMessage(errcode) << "."; 1556 goto error; 1557 } 1558 } else { 1559 msg << "Error getting SCPD Document from " << 1560 service.GetAbsSCPDURL() << "."; 1561 AddLogLineU(true, logUPnP, msg); 1562 } 1563 1564 return; 1565 1566 // Error processing 1567error: 1568 AddLogLineU(true, logUPnP, msg); 1569} 1570 1571 1572void CUPnPControlPoint::Unsubscribe(CUPnPService &service) 1573{ 1574 ServiceMap::iterator it = m_ServiceMap.find(service.GetAbsEventSubURL()); 1575 if (it != m_ServiceMap.end()) { 1576 m_ServiceMap.erase(it); 1577 UpnpUnSubscribe(m_UPnPClientHandle, service.GetSID()); 1578 } 1579} 1580 1581#endif /* ENABLE_UPNP */ 1582