Improve speed of category tab title updates
[amule.git] / src / UPnPBase.cpp
blobb89c6dd1e9b06b884cb6090179b6dbe16925a2ff
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2004-2008 Marcelo Roberto Jimenez ( phoenix@amule.org )
5 // Copyright (c) 2006-2008 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.
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.
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
26 #ifdef HAVE_CONFIG_H
27 # include "config.h" // Needed for ENABLE_UPNP
28 #endif
30 #ifdef ENABLE_UPNP
32 #define UPNP_C
34 #include "UPnPBase.h"
37 #include <dlfcn.h> // For dlopen(), dlsym(), dlclose()
38 #include <algorithm> // For transform()
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
52 /**
53 * Case insensitive std::string comparison
55 bool stdStringIsEqualCI(const std::string &s1, const std::string &s2)
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;
65 CUPnPPortMapping::CUPnPPortMapping(
66 int port,
67 const std::string &protocol,
68 bool enabled,
69 const std::string &description)
71 m_port(),
72 m_protocol(protocol),
73 m_enabled(enabled ? "1" : "0"),
74 m_description(description),
75 m_key()
77 std::ostringstream oss;
78 oss << port;
79 m_port = oss.str();
80 m_key = m_protocol + m_port;
84 const std::string &CUPnPLib::UPNP_ROOT_DEVICE =
85 "upnp:rootdevice";
87 const std::string &CUPnPLib::UPNP_DEVICE_IGW =
88 "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
89 const std::string &CUPnPLib::UPNP_DEVICE_WAN =
90 "urn:schemas-upnp-org:device:WANDevice:1";
91 const std::string &CUPnPLib::UPNP_DEVICE_WAN_CONNECTION =
92 "urn:schemas-upnp-org:device:WANConnectionDevice:1";
93 const std::string &CUPnPLib::UPNP_DEVICE_LAN =
94 "urn:schemas-upnp-org:device:LANDevice:1";
96 const std::string &CUPnPLib::UPNP_SERVICE_LAYER3_FORWARDING =
97 "urn:schemas-upnp-org:service:Layer3Forwarding:1";
98 const std::string &CUPnPLib::UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG =
99 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1";
100 const std::string &CUPnPLib::UPNP_SERVICE_WAN_IP_CONNECTION =
101 "urn:schemas-upnp-org:service:WANIPConnection:1";
102 const std::string &CUPnPLib::UPNP_SERVICE_WAN_PPP_CONNECTION =
103 "urn:schemas-upnp-org:service:WANPPPConnection:1";
106 CUPnPLib::CUPnPLib(CUPnPControlPoint &ctrlPoint)
108 m_ctrlPoint(ctrlPoint)
113 std::string CUPnPLib::GetUPnPErrorMessage(int code) const
115 return UpnpGetErrorMessage(code);
119 std::string CUPnPLib::processUPnPErrorMessage(
120 const std::string &messsage,
121 int errorCode,
122 const DOMString errorString,
123 IXML_Document *doc) const
125 std::ostringstream msg;
126 if (errorString == NULL || *errorString == 0) {
127 errorString = "Not available";
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 "'.";
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);
155 return msg.str();
159 void CUPnPLib::ProcessActionResponse(
160 IXML_Document *RespDoc,
161 const std::string &actionName) const
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);
176 } else {
177 msg << "\n Empty response for action '" <<
178 actionName << "'.";
180 AddDebugLogLineN(logUPnP, msg);
185 * \brief Returns the root node of a given document.
187 IXML_Element *CUPnPLib::Element_GetRootElement(
188 IXML_Document *doc) const
190 IXML_Element *root = REINTERPRET_CAST(IXML_Element *)(
191 ixmlNode_getFirstChild(
192 REINTERPRET_CAST(IXML_Node *)(doc)));
194 return root;
199 * \brief Returns the first child of a given element.
201 IXML_Element *CUPnPLib::Element_GetFirstChild(
202 IXML_Element *parent) const
204 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(parent);
205 IXML_Node *child = ixmlNode_getFirstChild(node);
207 return REINTERPRET_CAST(IXML_Element *)(child);
212 * \brief Returns the next sibling of a given child.
214 IXML_Element *CUPnPLib::Element_GetNextSibling(
215 IXML_Element *child) const
217 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(child);
218 IXML_Node *sibling = ixmlNode_getNextSibling(node);
220 return REINTERPRET_CAST(IXML_Element *)(sibling);
225 * \brief Returns the element tag (name)
227 const DOMString CUPnPLib::Element_GetTag(
228 IXML_Element *element) const
230 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element);
231 const DOMString tag = ixmlNode_getNodeName(node);
233 return tag;
238 * \brief Returns the TEXT node value of the current node.
240 const std::string CUPnPLib::Element_GetTextValue(
241 IXML_Element *element) const
243 if (!element) {
244 return stdEmptyString;
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;
254 return ret;
259 * \brief Returns the TEXT node value of the first child matching tag.
261 const std::string CUPnPLib::Element_GetChildValueByTag(
262 IXML_Element *element,
263 const DOMString tag) const
265 IXML_Element *child =
266 Element_GetFirstChildByTag(element, tag);
268 return Element_GetTextValue(child);
273 * \brief Returns the first child element that matches the requested tag or
274 * NULL if not found.
276 IXML_Element *CUPnPLib::Element_GetFirstChildByTag(
277 IXML_Element *element,
278 const DOMString tag) const
280 if (!element || !tag) {
281 return NULL;
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);
292 return REINTERPRET_CAST(IXML_Element *)(child);
297 * \brief Returns the next sibling element that matches the requested tag. Should be
298 * used with the return value of Element_GetFirstChildByTag().
300 IXML_Element *CUPnPLib::Element_GetNextSiblingByTag(
301 IXML_Element *element, const DOMString tag) const
303 if (!element || !tag) {
304 return NULL;
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));
314 return REINTERPRET_CAST(IXML_Element *)(child);
318 const std::string CUPnPLib::Element_GetAttributeByTag(
319 IXML_Element *element, const DOMString tag) const
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;
329 ixmlNamedNodeMap_free(NamedNodeMap);
331 return ret;
335 CUPnPError::CUPnPError(
336 const CUPnPLib &upnpLib,
337 IXML_Document *errorDoc)
339 m_root (upnpLib.Element_GetRootElement(errorDoc)),
340 m_ErrorCode (upnpLib.Element_GetChildValueByTag(m_root, "errorCode")),
341 m_ErrorDescription(upnpLib.Element_GetChildValueByTag(m_root, "errorDescription"))
346 CUPnPArgument::CUPnPArgument(
347 const CUPnPControlPoint &upnpControlPoint,
348 CUPnPLib &upnpLib,
349 IXML_Element *argument,
350 const std::string &WXUNUSED(SCPDURL))
352 m_UPnPControlPoint(upnpControlPoint),
353 m_name (upnpLib.Element_GetChildValueByTag(argument, "name")),
354 m_direction (upnpLib.Element_GetChildValueByTag(argument, "direction")),
355 m_retval (upnpLib.Element_GetFirstChildByTag(argument, "retval")),
356 m_relatedStateVariable(upnpLib.Element_GetChildValueByTag(argument, "relatedStateVariable"))
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);
368 CUPnPAction::CUPnPAction(
369 const CUPnPControlPoint &upnpControlPoint,
370 CUPnPLib &upnpLib,
371 IXML_Element *action,
372 const std::string &SCPDURL)
374 m_UPnPControlPoint(upnpControlPoint),
375 m_ArgumentList(upnpControlPoint, upnpLib, action, SCPDURL),
376 m_name(upnpLib.Element_GetChildValueByTag(action, "name"))
378 std::ostringstream msg;
379 msg << "\n Action:" <<
380 "\n name: " << m_name;
381 AddDebugLogLineN(logUPnP, msg);
385 CUPnPAllowedValue::CUPnPAllowedValue(
386 const CUPnPControlPoint &upnpControlPoint,
387 CUPnPLib &upnpLib,
388 IXML_Element *allowedValue,
389 const std::string &WXUNUSED(SCPDURL))
391 m_UPnPControlPoint(upnpControlPoint),
392 m_allowedValue(upnpLib.Element_GetTextValue(allowedValue))
394 std::ostringstream msg;
395 msg << "\n AllowedValue:" <<
396 "\n allowedValue: " << m_allowedValue;
397 AddDebugLogLineN(logUPnP, msg);
401 CUPnPStateVariable::CUPnPStateVariable(
402 const CUPnPControlPoint &upnpControlPoint,
403 CUPnPLib &upnpLib,
404 IXML_Element *stateVariable,
405 const std::string &SCPDURL)
407 m_UPnPControlPoint(upnpControlPoint),
408 m_AllowedValueList(upnpControlPoint, upnpLib, stateVariable, SCPDURL),
409 m_name (upnpLib.Element_GetChildValueByTag(stateVariable, "name")),
410 m_dataType (upnpLib.Element_GetChildValueByTag(stateVariable, "dataType")),
411 m_defaultValue(upnpLib.Element_GetChildValueByTag(stateVariable, "defaultValue")),
412 m_sendEvents (upnpLib.Element_GetAttributeByTag (stateVariable, "sendEvents"))
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);
424 CUPnPSCPD::CUPnPSCPD(
425 const CUPnPControlPoint &upnpControlPoint,
426 CUPnPLib &upnpLib,
427 IXML_Element *scpd,
428 const std::string &SCPDURL)
430 m_UPnPControlPoint(upnpControlPoint),
431 m_ActionList(upnpControlPoint, upnpLib, scpd, SCPDURL),
432 m_ServiceStateTable(upnpControlPoint, upnpLib, scpd, SCPDURL),
433 m_SCPDURL(SCPDURL)
438 CUPnPArgumentValue::CUPnPArgumentValue()
440 m_argument(),
441 m_value()
446 CUPnPArgumentValue::CUPnPArgumentValue(
447 const std::string &argument, const std::string &value)
449 m_argument(argument),
450 m_value(value)
455 CUPnPService::CUPnPService(
456 const CUPnPControlPoint &upnpControlPoint,
457 CUPnPLib &upnpLib,
458 IXML_Element *service,
459 const std::string &URLBase)
461 m_UPnPControlPoint(upnpControlPoint),
462 m_upnpLib(upnpLib),
463 m_serviceType(upnpLib.Element_GetChildValueByTag(service, "serviceType")),
464 m_serviceId (upnpLib.Element_GetChildValueByTag(service, "serviceId")),
465 m_SCPDURL (upnpLib.Element_GetChildValueByTag(service, "SCPDURL")),
466 m_controlURL (upnpLib.Element_GetChildValueByTag(service, "controlURL")),
467 m_eventSubURL(upnpLib.Element_GetChildValueByTag(service, "eventSubURL")),
468 m_timeout(1801),
469 m_SCPD(NULL)
471 std::ostringstream msg;
472 int errcode;
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;
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;
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;
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);
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);
563 #endif
564 } else {
565 msg.str("");
566 msg << "Uninteresting service detected: '" <<
567 m_serviceType << "'. Ignoring.";
568 AddDebugLogLineC(logUPnP, msg);
573 CUPnPService::~CUPnPService()
578 bool CUPnPService::Execute(
579 const std::string &ActionName,
580 const std::vector<CUPnPArgumentValue> &ArgValue) const
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;
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;
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;
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;
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;
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;
651 if (firstTime) {
652 firstTime = false;
653 } else {
654 msgAction << ", ";
656 msgAction <<
657 ArgValue[i].GetArgument() <<
658 "='" <<
659 ArgValue[i].GetValue() <<
660 "'";
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;
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;
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
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;
717 ixmlDocument_free(ActionDoc);
719 // Check the response document
720 m_upnpLib.ProcessActionResponse(
721 RespDoc, action.GetName());
723 // Free the response document
724 ixmlDocument_free(RespDoc);
726 return true;
730 const std::string CUPnPService::GetStateVariable(
731 const std::string &stateVariableName) const
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;
748 msg << "GetStateVariable: " <<
749 stateVariableName <<
750 "='" <<
751 StVarVal <<
752 "'.";
753 AddDebugLogLineN(logUPnP, msg);
754 return stdEmptyString;
758 CUPnPDevice::CUPnPDevice(
759 const CUPnPControlPoint &upnpControlPoint,
760 CUPnPLib &upnpLib,
761 IXML_Element *device,
762 const std::string &URLBase)
764 m_UPnPControlPoint(upnpControlPoint),
765 m_DeviceList(upnpControlPoint, upnpLib, device, URLBase),
766 m_ServiceList(upnpControlPoint, upnpLib, device, URLBase),
767 m_deviceType (upnpLib.Element_GetChildValueByTag(device, "deviceType")),
768 m_friendlyName (upnpLib.Element_GetChildValueByTag(device, "friendlyName")),
769 m_manufacturer (upnpLib.Element_GetChildValueByTag(device, "manufacturer")),
770 m_manufacturerURL (upnpLib.Element_GetChildValueByTag(device, "manufacturerURL")),
771 m_modelDescription (upnpLib.Element_GetChildValueByTag(device, "modelDescription")),
772 m_modelName (upnpLib.Element_GetChildValueByTag(device, "modelName")),
773 m_modelNumber (upnpLib.Element_GetChildValueByTag(device, "modelNumber")),
774 m_modelURL (upnpLib.Element_GetChildValueByTag(device, "modelURL")),
775 m_serialNumber (upnpLib.Element_GetChildValueByTag(device, "serialNumber")),
776 m_UDN (upnpLib.Element_GetChildValueByTag(device, "UDN")),
777 m_UPC (upnpLib.Element_GetChildValueByTag(device, "UPC")),
778 m_presentationURL (upnpLib.Element_GetChildValueByTag(device, "presentationURL"))
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;
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);
816 CUPnPRootDevice::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)
825 CUPnPDevice(upnpControlPoint, upnpLib, rootDevice, FixedURLBase),
826 m_UPnPControlPoint(upnpControlPoint),
827 m_URLBase(OriginalURLBase),
828 m_location(location),
829 m_expires(expires)
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);
842 CUPnPControlPoint *CUPnPControlPoint::s_CtrlPoint = NULL;
845 CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort)
847 m_upnpLib(*this),
848 m_UPnPClientHandle(),
849 m_RootDeviceMap(),
850 m_ServiceMap(),
851 m_ActivePortMappingsMap(),
852 m_RootDeviceListMutex(),
853 m_IGWDeviceDetected(false),
854 m_WanService(NULL)
856 // Pointer to self
857 s_CtrlPoint = this;
858 // Null string at first
859 std::ostringstream msg;
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;
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;
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.
889 // But lets find out what we have in our network by calling UPNP_ROOT_DEVICE.
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;
903 // Wait for the UPnP initialization to complete.
905 // Lock the search timeout mutex
906 m_WaitForSearchTimeoutMutex.Lock();
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);
913 return;
915 // Error processing
916 error:
917 UpnpFinish();
918 msg << ret << ": " << m_upnpLib.GetUPnPErrorMessage(ret) << ".";
919 throw CUPnPException(msg);
923 CUPnPControlPoint::~CUPnPControlPoint()
925 for( RootDeviceMap::iterator it = m_RootDeviceMap.begin();
926 it != m_RootDeviceMap.end();
927 ++it) {
928 delete it->second;
930 // Remove all first
931 // RemoveAll();
932 UpnpUnRegisterClient(m_UPnPClientHandle);
933 UpnpFinish();
937 bool CUPnPControlPoint::AddPortMappings(
938 std::vector<CUPnPPortMapping> &upnpPortMapping)
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;
949 int n = upnpPortMapping.size();
950 bool ok = false;
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;
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];
967 // Add the port mapping
968 PrivateAddPortMapping(upnpPortMapping[i]);
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");
985 // Just for testing
986 std::vector<CUPnPArgumentValue> argval;
987 argval.resize(0);
988 m_WanService->Execute("GetStatusInfo", argval);
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
1002 // Debug only
1003 msg.str("");
1004 msg << "CUPnPControlPoint::DeletePortMappings: "
1005 "m_ActivePortMappingsMap.size() == " <<
1006 m_ActivePortMappingsMap.size();
1007 AddDebugLogLineN(logUPnP, msg);
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;
1017 return ok;
1021 void CUPnPControlPoint::RefreshPortMappings()
1023 for ( PortMappingMap::iterator it = m_ActivePortMappingsMap.begin();
1024 it != m_ActivePortMappingsMap.end();
1025 ++it) {
1026 PrivateAddPortMapping(it->second);
1029 // For testing
1030 m_WanService->GetStateVariable("PortMappingNumberOfEntries");
1034 bool CUPnPControlPoint::PrivateAddPortMapping(
1035 CUPnPPortMapping &upnpPortMapping)
1037 // Get an IP address. The UPnP server one must do.
1038 std::string ipAddress(UpnpGetServerIpAddress());
1040 // Start building the action
1041 std::string actionName("AddPortMapping");
1042 std::vector<CUPnPArgumentValue> argval(8);
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");
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);
1069 return ret;
1073 bool CUPnPControlPoint::DeletePortMappings(
1074 std::vector<CUPnPPortMapping> &upnpPortMapping)
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;
1085 int n = upnpPortMapping.size();
1086 bool ok = false;
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;
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);
1113 // Delete the port mapping
1114 PrivateDeletePortMapping(upnpPortMapping[i]);
1118 // Debug only
1119 msg.str("");
1120 msg << "CUPnPControlPoint::DeletePortMappings: "
1121 "m_ActivePortMappingsMap.size() == " <<
1122 m_ActivePortMappingsMap.size();
1123 AddDebugLogLineN(logUPnP, msg);
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;
1133 return ok;
1137 bool CUPnPControlPoint::PrivateDeletePortMapping(
1138 CUPnPPortMapping &upnpPortMapping)
1140 // Start building the action
1141 std::string actionName("DeletePortMapping");
1142 std::vector<CUPnPArgumentValue> argval(3);
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());
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);
1159 return ret;
1163 // This function is static
1164 int CUPnPControlPoint::Callback(Upnp_EventType EventType, void *Event, void * /*Cookie*/)
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;
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
1185 upnpDiscovery:
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);
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);
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);
1234 // Add the root device to our list
1235 upnpCP->AddRootDevice(rootDevice, urlBase,
1236 d_event->Location, d_event->Expires);
1238 // Free the XML doc tree
1239 ixmlDocument_free(doc);
1241 break;
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);
1249 // Unlock the search timeout mutex
1250 upnpCP->m_WaitForSearchTimeoutMutex.Unlock();
1252 break;
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);
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);
1270 break;
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;
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): ";
1292 upnpEventRenewalComplete:
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
1308 break;
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: ";
1320 upnpEventSubscriptionExpired:
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);
1356 break;
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>");
1374 /* No need for any processing here, just print out results.
1375 * Service state table updates are handled by events.
1377 break;
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
1399 break;
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): ";
1413 eventSubscriptionRequest:
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;
1431 return 0;
1435 void CUPnPControlPoint::OnEventReceived(
1436 const std::string &Sid,
1437 int EventKey,
1438 IXML_Document *ChangedVariablesDoc)
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);
1462 } else {
1463 msg << "\n Empty property list.";
1465 AddDebugLogLineC(logUPnP, msg);
1466 // Freeing that doc segfaults. Probably should not be freed.
1467 //ixmlDocument_free(ChangedVariablesDoc);
1471 void CUPnPControlPoint::AddRootDevice(
1472 IXML_Element *rootDevice, const std::string &urlBase,
1473 const char *location, int expires)
1475 // Lock the Root Device List
1476 CUPnPMutexLocker lock(m_RootDeviceListMutex);
1478 // Root node's URLBase
1479 std::string OriginalURLBase(urlBase);
1480 std::string FixedURLBase(OriginalURLBase.empty() ?
1481 location :
1482 OriginalURLBase);
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;
1503 void CUPnPControlPoint::RemoveRootDevice(const char *udn)
1505 // Lock the Root Device List
1506 CUPnPMutexLocker lock(m_RootDeviceListMutex);
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);
1518 void CUPnPControlPoint::Subscribe(CUPnPService &service)
1520 std::ostringstream msg;
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("");
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;
1558 } else {
1559 msg << "Error getting SCPD Document from " <<
1560 service.GetAbsSCPDURL() << ".";
1561 AddLogLineU(true, logUPnP, msg);
1564 return;
1566 // Error processing
1567 error:
1568 AddLogLineU(true, logUPnP, msg);
1572 void CUPnPControlPoint::Unsubscribe(CUPnPService &service)
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());
1581 #endif /* ENABLE_UPNP */