Update Wiki URL
[amule.git] / src / UPnPBase.cpp
blob5dd4da1675b895e0834c6465e9ba8e9dfbe36cef
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.
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.
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 // check for broken Debian-hacked libUPnP
33 #include <upnp.h>
34 #ifdef STRING_H // defined in UpnpString.h Yes, I would have liked UPNPSTRING_H much better.
35 #define BROKEN_DEBIAN_LIBUPNP
36 #endif
38 #include "UPnPBase.h"
40 #include <algorithm> // For transform()
42 #ifdef BROKEN_DEBIAN_LIBUPNP
43 #define GET_UPNP_STRING(a) UpnpString_get_String(a)
44 #else
45 #define GET_UPNP_STRING(a) (a)
46 #endif
48 std::string stdEmptyString;
50 const char s_argument[] = "argument";
51 const char s_argumentList[] = "argumentList";
52 const char s_action[] = "action";
53 const char s_actionList[] = "actionList";
54 const char s_allowedValue[] = "allowedValue";
55 const char s_allowedValueList[] = "allowedValueList";
56 const char s_stateVariable[] = "stateVariable";
57 const char s_serviceStateTable[] = "serviceStateTable";
58 const char s_service[] = "service";
59 const char s_serviceList[] = "serviceList";
60 const char s_device[] = "device";
61 const char s_deviceList[] = "deviceList";
63 /**
64 * Case insensitive std::string comparison
66 bool stdStringIsEqualCI(const std::string &s1, const std::string &s2)
68 std::string ns1(s1);
69 std::string ns2(s2);
70 std::transform(ns1.begin(), ns1.end(), ns1.begin(), tolower);
71 std::transform(ns2.begin(), ns2.end(), ns2.begin(), tolower);
72 return ns1 == ns2;
76 CUPnPPortMapping::CUPnPPortMapping(
77 int port,
78 const std::string &protocol,
79 bool enabled,
80 const std::string &description)
82 m_port(),
83 m_protocol(protocol),
84 m_enabled(enabled ? "1" : "0"),
85 m_description(description),
86 m_key()
88 std::ostringstream oss;
89 oss << port;
90 m_port = oss.str();
91 m_key = m_protocol + m_port;
95 const std::string &CUPnPLib::UPNP_ROOT_DEVICE =
96 "upnp:rootdevice";
98 const std::string &CUPnPLib::UPNP_DEVICE_IGW =
99 "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
100 const std::string &CUPnPLib::UPNP_DEVICE_WAN =
101 "urn:schemas-upnp-org:device:WANDevice:1";
102 const std::string &CUPnPLib::UPNP_DEVICE_WAN_CONNECTION =
103 "urn:schemas-upnp-org:device:WANConnectionDevice:1";
104 const std::string &CUPnPLib::UPNP_DEVICE_LAN =
105 "urn:schemas-upnp-org:device:LANDevice:1";
107 const std::string &CUPnPLib::UPNP_SERVICE_LAYER3_FORWARDING =
108 "urn:schemas-upnp-org:service:Layer3Forwarding:1";
109 const std::string &CUPnPLib::UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG =
110 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1";
111 const std::string &CUPnPLib::UPNP_SERVICE_WAN_IP_CONNECTION =
112 "urn:schemas-upnp-org:service:WANIPConnection:1";
113 const std::string &CUPnPLib::UPNP_SERVICE_WAN_PPP_CONNECTION =
114 "urn:schemas-upnp-org:service:WANPPPConnection:1";
117 CUPnPLib::CUPnPLib(CUPnPControlPoint &ctrlPoint)
119 m_ctrlPoint(ctrlPoint)
124 std::string CUPnPLib::GetUPnPErrorMessage(int code) const
126 return UpnpGetErrorMessage(code);
130 std::string CUPnPLib::processUPnPErrorMessage(
131 const std::string &messsage,
132 int errorCode,
133 const DOMString errorString,
134 IXML_Document *doc) const
136 std::ostringstream msg;
137 if (errorString == NULL || *errorString == 0) {
138 errorString = "Not available";
140 if (errorCode > 0) {
141 msg << "Error: " <<
142 messsage <<
143 ": Error code :'";
144 if (doc) {
145 CUPnPError e(*this, doc);
146 msg << e.getErrorCode() <<
147 "', Error description :'" <<
148 e.getErrorDescription() <<
149 "'.";
150 } else {
151 msg << errorCode <<
152 "', Error description :'" <<
153 errorString <<
154 "'.";
156 AddDebugLogLineN(logUPnP, msg);
157 } else {
158 msg << "Error: " <<
159 messsage <<
160 ": UPnP SDK error: " <<
161 GetUPnPErrorMessage(errorCode) <<
162 " (" << errorCode << ").";
163 AddDebugLogLineN(logUPnP, msg);
166 return msg.str();
170 void CUPnPLib::ProcessActionResponse(
171 IXML_Document *RespDoc,
172 const std::string &actionName) const
174 std::ostringstream msg;
175 msg << "Response: ";
176 IXML_Element *root = Element_GetRootElement(RespDoc);
177 IXML_Element *child = Element_GetFirstChild(root);
178 if (child) {
179 while (child) {
180 const DOMString childTag = Element_GetTag(child);
181 std::string childValue = Element_GetTextValue(child);
182 msg << "\n " <<
183 childTag << "='" <<
184 childValue << "'";
185 child = Element_GetNextSibling(child);
187 } else {
188 msg << "\n Empty response for action '" <<
189 actionName << "'.";
191 AddDebugLogLineN(logUPnP, msg);
196 * \brief Returns the root node of a given document.
198 IXML_Element *CUPnPLib::Element_GetRootElement(
199 IXML_Document *doc) const
201 IXML_Element *root = reinterpret_cast<IXML_Element *>(
202 ixmlNode_getFirstChild(&doc->n));
204 return root;
209 * \brief Returns the first child of a given element.
211 IXML_Element *CUPnPLib::Element_GetFirstChild(
212 IXML_Element *parent) const
214 IXML_Node *child = ixmlNode_getFirstChild(&parent->n);
216 return reinterpret_cast<IXML_Element *>(child);
221 * \brief Returns the next sibling of a given child.
223 IXML_Element *CUPnPLib::Element_GetNextSibling(
224 IXML_Element *child) const
226 IXML_Node *sibling = ixmlNode_getNextSibling(&child->n);
228 return reinterpret_cast<IXML_Element *>(sibling);
233 * \brief Returns the element tag (name)
235 const DOMString CUPnPLib::Element_GetTag(
236 IXML_Element *element) const
238 const DOMString tag = ixmlNode_getNodeName(&element->n);
240 return tag;
245 * \brief Returns the TEXT node value of the current node.
247 const std::string CUPnPLib::Element_GetTextValue(
248 IXML_Element *element) const
250 if (!element) {
251 return stdEmptyString;
253 IXML_Node *text = ixmlNode_getFirstChild(&element->n);
254 const DOMString s = ixmlNode_getNodeValue(text);
255 std::string ret;
256 if (s) {
257 ret = s;
260 return ret;
265 * \brief Returns the TEXT node value of the first child matching tag.
267 const std::string CUPnPLib::Element_GetChildValueByTag(
268 IXML_Element *element,
269 const DOMString tag) const
271 IXML_Element *child =
272 Element_GetFirstChildByTag(element, tag);
274 return Element_GetTextValue(child);
279 * \brief Returns the first child element that matches the requested tag or
280 * NULL if not found.
282 IXML_Element *CUPnPLib::Element_GetFirstChildByTag(
283 IXML_Element *element,
284 const DOMString tag) const
286 if (!element || !tag) {
287 return NULL;
290 IXML_Node *child = ixmlNode_getFirstChild(&element->n);
291 const DOMString childTag = ixmlNode_getNodeName(child);
292 while(child && childTag && strcmp(tag, childTag)) {
293 child = ixmlNode_getNextSibling(child);
294 childTag = ixmlNode_getNodeName(child);
297 return reinterpret_cast<IXML_Element *>(child);
302 * \brief Returns the next sibling element that matches the requested tag. Should be
303 * used with the return value of Element_GetFirstChildByTag().
305 IXML_Element *CUPnPLib::Element_GetNextSiblingByTag(
306 IXML_Element *element, const DOMString tag) const
308 if (!element || !tag) {
309 return NULL;
312 IXML_Node *child = &element->n;
313 const DOMString childTag = NULL;
314 do {
315 child = ixmlNode_getNextSibling(child);
316 childTag = ixmlNode_getNodeName(child);
317 } while(child && childTag && strcmp(tag, childTag));
319 return reinterpret_cast<IXML_Element *>(child);
323 const std::string CUPnPLib::Element_GetAttributeByTag(
324 IXML_Element *element, const DOMString tag) const
326 IXML_NamedNodeMap *NamedNodeMap = ixmlNode_getAttributes(&element->n);
327 IXML_Node *attribute = ixmlNamedNodeMap_getNamedItem(NamedNodeMap, tag);
328 const DOMString s = ixmlNode_getNodeValue(attribute);
329 std::string ret;
330 if (s) {
331 ret = s;
333 ixmlNamedNodeMap_free(NamedNodeMap);
335 return ret;
339 CUPnPError::CUPnPError(
340 const CUPnPLib &upnpLib,
341 IXML_Document *errorDoc)
343 m_root (upnpLib.Element_GetRootElement(errorDoc)),
344 m_ErrorCode (upnpLib.Element_GetChildValueByTag(m_root, "errorCode")),
345 m_ErrorDescription(upnpLib.Element_GetChildValueByTag(m_root, "errorDescription"))
350 CUPnPArgument::CUPnPArgument(
351 const CUPnPControlPoint &upnpControlPoint,
352 CUPnPLib &upnpLib,
353 IXML_Element *argument,
354 const std::string &WXUNUSED(SCPDURL))
356 m_UPnPControlPoint(upnpControlPoint),
357 m_name (upnpLib.Element_GetChildValueByTag(argument, "name")),
358 m_direction (upnpLib.Element_GetChildValueByTag(argument, "direction")),
359 m_retval (upnpLib.Element_GetFirstChildByTag(argument, "retval")),
360 m_relatedStateVariable(upnpLib.Element_GetChildValueByTag(argument, "relatedStateVariable"))
362 std::ostringstream msg;
363 msg << "\n Argument:" <<
364 "\n name: " << m_name <<
365 "\n direction: " << m_direction <<
366 "\n retval: " << m_retval <<
367 "\n relatedStateVariable: " << m_relatedStateVariable;
368 AddDebugLogLineN(logUPnP, msg);
372 CUPnPAction::CUPnPAction(
373 const CUPnPControlPoint &upnpControlPoint,
374 CUPnPLib &upnpLib,
375 IXML_Element *action,
376 const std::string &SCPDURL)
378 m_UPnPControlPoint(upnpControlPoint),
379 m_ArgumentList(upnpControlPoint, upnpLib, action, SCPDURL),
380 m_name(upnpLib.Element_GetChildValueByTag(action, "name"))
382 std::ostringstream msg;
383 msg << "\n Action:" <<
384 "\n name: " << m_name;
385 AddDebugLogLineN(logUPnP, msg);
389 CUPnPAllowedValue::CUPnPAllowedValue(
390 const CUPnPControlPoint &upnpControlPoint,
391 CUPnPLib &upnpLib,
392 IXML_Element *allowedValue,
393 const std::string &WXUNUSED(SCPDURL))
395 m_UPnPControlPoint(upnpControlPoint),
396 m_allowedValue(upnpLib.Element_GetTextValue(allowedValue))
398 std::ostringstream msg;
399 msg << "\n AllowedValue:" <<
400 "\n allowedValue: " << m_allowedValue;
401 AddDebugLogLineN(logUPnP, msg);
405 CUPnPStateVariable::CUPnPStateVariable(
406 const CUPnPControlPoint &upnpControlPoint,
407 CUPnPLib &upnpLib,
408 IXML_Element *stateVariable,
409 const std::string &SCPDURL)
411 m_UPnPControlPoint(upnpControlPoint),
412 m_AllowedValueList(upnpControlPoint, upnpLib, stateVariable, SCPDURL),
413 m_name (upnpLib.Element_GetChildValueByTag(stateVariable, "name")),
414 m_dataType (upnpLib.Element_GetChildValueByTag(stateVariable, "dataType")),
415 m_defaultValue(upnpLib.Element_GetChildValueByTag(stateVariable, "defaultValue")),
416 m_sendEvents (upnpLib.Element_GetAttributeByTag (stateVariable, "sendEvents"))
418 std::ostringstream msg;
419 msg << "\n StateVariable:" <<
420 "\n name: " << m_name <<
421 "\n dataType: " << m_dataType <<
422 "\n defaultValue: " << m_defaultValue <<
423 "\n sendEvents: " << m_sendEvents;
424 AddDebugLogLineN(logUPnP, msg);
428 CUPnPSCPD::CUPnPSCPD(
429 const CUPnPControlPoint &upnpControlPoint,
430 CUPnPLib &upnpLib,
431 IXML_Element *scpd,
432 const std::string &SCPDURL)
434 m_UPnPControlPoint(upnpControlPoint),
435 m_ActionList(upnpControlPoint, upnpLib, scpd, SCPDURL),
436 m_ServiceStateTable(upnpControlPoint, upnpLib, scpd, SCPDURL),
437 m_SCPDURL(SCPDURL)
442 CUPnPArgumentValue::CUPnPArgumentValue()
444 m_argument(),
445 m_value()
450 CUPnPArgumentValue::CUPnPArgumentValue(
451 const std::string &argument, const std::string &value)
453 m_argument(argument),
454 m_value(value)
459 CUPnPService::CUPnPService(
460 const CUPnPControlPoint &upnpControlPoint,
461 CUPnPLib &upnpLib,
462 IXML_Element *service,
463 const std::string &URLBase)
465 m_UPnPControlPoint(upnpControlPoint),
466 m_upnpLib(upnpLib),
467 m_serviceType(upnpLib.Element_GetChildValueByTag(service, "serviceType")),
468 m_serviceId (upnpLib.Element_GetChildValueByTag(service, "serviceId")),
469 m_SCPDURL (upnpLib.Element_GetChildValueByTag(service, "SCPDURL")),
470 m_controlURL (upnpLib.Element_GetChildValueByTag(service, "controlURL")),
471 m_eventSubURL(upnpLib.Element_GetChildValueByTag(service, "eventSubURL")),
472 m_timeout(1801),
473 m_SCPD(NULL)
475 std::ostringstream msg;
476 int errcode;
478 std::vector<char> vscpdURL(URLBase.length() + m_SCPDURL.length() + 1);
479 char *scpdURL = &vscpdURL[0];
480 errcode = UpnpResolveURL(
481 URLBase.c_str(),
482 m_SCPDURL.c_str(),
483 scpdURL);
484 if( errcode != UPNP_E_SUCCESS ) {
485 msg << "Error generating scpdURL from " <<
486 "|" << URLBase << "|" <<
487 m_SCPDURL << "|.";
488 AddDebugLogLineN(logUPnP, msg);
489 } else {
490 m_absSCPDURL = scpdURL;
493 std::vector<char> vcontrolURL(
494 URLBase.length() + m_controlURL.length() + 1);
495 char *controlURL = &vcontrolURL[0];
496 errcode = UpnpResolveURL(
497 URLBase.c_str(),
498 m_controlURL.c_str(),
499 controlURL);
500 if( errcode != UPNP_E_SUCCESS ) {
501 msg << "Error generating controlURL from " <<
502 "|" << URLBase << "|" <<
503 m_controlURL << "|.";
504 AddDebugLogLineN(logUPnP, msg);
505 } else {
506 m_absControlURL = controlURL;
509 std::vector<char> veventURL(
510 URLBase.length() + m_eventSubURL.length() + 1);
511 char *eventURL = &veventURL[0];
512 errcode = UpnpResolveURL(
513 URLBase.c_str(),
514 m_eventSubURL.c_str(),
515 eventURL);
516 if( errcode != UPNP_E_SUCCESS ) {
517 msg << "Error generating eventURL from " <<
518 "|" << URLBase << "|" <<
519 m_eventSubURL << "|.";
520 AddDebugLogLineN(logUPnP, msg);
521 } else {
522 m_absEventSubURL = eventURL;
525 msg << "\n Service:" <<
526 "\n serviceType: " << m_serviceType <<
527 "\n serviceId: " << m_serviceId <<
528 "\n SCPDURL: " << m_SCPDURL <<
529 "\n absSCPDURL: " << m_absSCPDURL <<
530 "\n controlURL: " << m_controlURL <<
531 "\n absControlURL: " << m_absControlURL <<
532 "\n eventSubURL: " << m_eventSubURL <<
533 "\n absEventSubURL: " << m_absEventSubURL;
534 AddDebugLogLineN(logUPnP, msg);
536 if (m_serviceType == upnpLib.UPNP_SERVICE_WAN_IP_CONNECTION ||
537 m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION) {
538 #if 0
539 m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION ||
540 m_serviceType == upnpLib.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG ||
541 m_serviceType == upnpLib.UPNP_SERVICE_LAYER3_FORWARDING) {
542 #endif
543 #if 0
544 //#warning Delete this code on release.
545 if (!upnpLib.m_ctrlPoint.WanServiceDetected()) {
546 // This condition can be used to suspend the parse
547 // of the XML tree.
548 #endif
549 //#warning Delete this code when m_WanService is no longer used.
550 upnpLib.m_ctrlPoint.SetWanService(this);
551 // Log it
552 msg.str("");
553 msg << "WAN Service Detected: '" <<
554 m_serviceType << "'.";
555 AddDebugLogLineC(logUPnP, msg);
556 // Subscribe
557 upnpLib.m_ctrlPoint.Subscribe(*this);
558 #if 0
559 //#warning Delete this code on release.
560 } else {
561 msg.str("");
562 msg << "WAN service detected again: '" <<
563 m_serviceType <<
564 "'. Will only use the first instance.";
565 AddDebugLogLineC(logUPnP, msg);
567 #endif
568 } else {
569 msg.str("");
570 msg << "Uninteresting service detected: '" <<
571 m_serviceType << "'. Ignoring.";
572 AddDebugLogLineC(logUPnP, msg);
577 CUPnPService::~CUPnPService()
582 bool CUPnPService::Execute(
583 const std::string &ActionName,
584 const std::vector<CUPnPArgumentValue> &ArgValue) const
586 std::ostringstream msg;
587 if (m_SCPD.get() == NULL) {
588 msg << "Service without SCPD Document, cannot execute action '" << ActionName <<
589 "' for service '" << GetServiceType() << "'.";
590 AddDebugLogLineN(logUPnP, msg);
591 return false;
593 std::ostringstream msgAction("Sending action ");
594 // Check for correct action name
595 ActionList::const_iterator itAction =
596 m_SCPD->GetActionList().find(ActionName);
597 if (itAction == m_SCPD->GetActionList().end()) {
598 msg << "Invalid action name '" << ActionName <<
599 "' for service '" << GetServiceType() << "'.";
600 AddDebugLogLineN(logUPnP, msg);
601 return false;
603 msgAction << ActionName << "(";
604 bool firstTime = true;
605 // Check for correct Argument/Value pairs
606 const CUPnPAction &action = *(itAction->second);
607 for (unsigned int i = 0; i < ArgValue.size(); ++i) {
608 ArgumentList::const_iterator itArg =
609 action.GetArgumentList().find(ArgValue[i].GetArgument());
610 if (itArg == action.GetArgumentList().end()) {
611 msg << "Invalid argument name '" << ArgValue[i].GetArgument() <<
612 "' for action '" << action.GetName() <<
613 "' for service '" << GetServiceType() << "'.";
614 AddDebugLogLineN(logUPnP, msg);
615 return false;
617 const CUPnPArgument &argument = *(itArg->second);
618 if (tolower(argument.GetDirection()[0]) != 'i' ||
619 tolower(argument.GetDirection()[1]) != 'n') {
620 msg << "Invalid direction for argument '" <<
621 ArgValue[i].GetArgument() <<
622 "' for action '" << action.GetName() <<
623 "' for service '" << GetServiceType() << "'.";
624 AddDebugLogLineN(logUPnP, msg);
625 return false;
627 const std::string relatedStateVariableName =
628 argument.GetRelatedStateVariable();
629 if (!relatedStateVariableName.empty()) {
630 ServiceStateTable::const_iterator itSVT =
631 m_SCPD->GetServiceStateTable().
632 find(relatedStateVariableName);
633 if (itSVT == m_SCPD->GetServiceStateTable().end()) {
634 msg << "Inconsistent Service State Table, did not find '" <<
635 relatedStateVariableName <<
636 "' for argument '" << argument.GetName() <<
637 "' for action '" << action.GetName() <<
638 "' for service '" << GetServiceType() << "'.";
639 AddDebugLogLineN(logUPnP, msg);
640 return false;
642 const CUPnPStateVariable &stateVariable = *(itSVT->second);
643 if ( !stateVariable.GetAllowedValueList().empty() &&
644 stateVariable.GetAllowedValueList().find(ArgValue[i].GetValue()) ==
645 stateVariable.GetAllowedValueList().end()) {
646 msg << "Value not allowed '" << ArgValue[i].GetValue() <<
647 "' for state variable '" << relatedStateVariableName <<
648 "' for argument '" << argument.GetName() <<
649 "' for action '" << action.GetName() <<
650 "' for service '" << GetServiceType() << "'.";
651 AddDebugLogLineN(logUPnP, msg);
652 return false;
655 if (firstTime) {
656 firstTime = false;
657 } else {
658 msgAction << ", ";
660 msgAction <<
661 ArgValue[i].GetArgument() <<
662 "='" <<
663 ArgValue[i].GetValue() <<
664 "'";
666 msgAction << ")";
667 AddDebugLogLineN(logUPnP, msgAction);
668 // Everything is ok, make the action
669 IXML_Document *ActionDoc = NULL;
670 if (!ArgValue.empty()) {
671 for (unsigned int i = 0; i < ArgValue.size(); ++i) {
672 int ret = UpnpAddToAction(
673 &ActionDoc,
674 action.GetName().c_str(),
675 GetServiceType().c_str(),
676 ArgValue[i].GetArgument().c_str(),
677 ArgValue[i].GetValue().c_str());
678 if (ret != UPNP_E_SUCCESS) {
679 m_upnpLib.processUPnPErrorMessage(
680 "UpnpAddToAction", ret, NULL, NULL);
681 return false;
684 } else {
685 ActionDoc = UpnpMakeAction(
686 action.GetName().c_str(),
687 GetServiceType().c_str(),
688 0, NULL);
689 if (!ActionDoc) {
690 msg << "Error: UpnpMakeAction returned NULL.";
691 AddDebugLogLineN(logUPnP, msg);
692 return false;
695 #if 0
696 // Send the action asynchronously
697 UpnpSendActionAsync(
698 m_UPnPControlPoint.GetUPnPClientHandle(),
699 GetAbsControlURL().c_str(),
700 GetServiceType().c_str(),
701 NULL, ActionDoc,
702 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
703 NULL);
704 return true;
705 #endif
707 // Send the action synchronously
708 IXML_Document *RespDoc = NULL;
709 int ret = UpnpSendAction(
710 m_UPnPControlPoint.GetUPnPClientHandle(),
711 GetAbsControlURL().c_str(),
712 GetServiceType().c_str(),
713 NULL, ActionDoc, &RespDoc);
714 if (ret != UPNP_E_SUCCESS) {
715 m_upnpLib.processUPnPErrorMessage(
716 "UpnpSendAction", ret, NULL, RespDoc);
717 ixmlDocument_free(ActionDoc);
718 ixmlDocument_free(RespDoc);
719 return false;
721 ixmlDocument_free(ActionDoc);
723 // Check the response document
724 m_upnpLib.ProcessActionResponse(
725 RespDoc, action.GetName());
727 // Free the response document
728 ixmlDocument_free(RespDoc);
730 return true;
734 const std::string CUPnPService::GetStateVariable(
735 const std::string &stateVariableName) const
737 std::ostringstream msg;
738 DOMString StVarVal;
739 int ret = UpnpGetServiceVarStatus(
740 m_UPnPControlPoint.GetUPnPClientHandle(),
741 GetAbsControlURL().c_str(),
742 stateVariableName.c_str(),
743 &StVarVal);
744 if (ret != UPNP_E_SUCCESS) {
745 msg << "GetStateVariable(\"" <<
746 stateVariableName <<
747 "\"): in a call to UpnpGetServiceVarStatus";
748 m_upnpLib.processUPnPErrorMessage(
749 msg.str(), ret, StVarVal, NULL);
750 return stdEmptyString;
752 msg << "GetStateVariable: " <<
753 stateVariableName <<
754 "='" <<
755 StVarVal <<
756 "'.";
757 AddDebugLogLineN(logUPnP, msg);
758 return stdEmptyString;
762 CUPnPDevice::CUPnPDevice(
763 const CUPnPControlPoint &upnpControlPoint,
764 CUPnPLib &upnpLib,
765 IXML_Element *device,
766 const std::string &URLBase)
768 m_UPnPControlPoint(upnpControlPoint),
769 m_DeviceList(upnpControlPoint, upnpLib, device, URLBase),
770 m_ServiceList(upnpControlPoint, upnpLib, device, URLBase),
771 m_deviceType (upnpLib.Element_GetChildValueByTag(device, "deviceType")),
772 m_friendlyName (upnpLib.Element_GetChildValueByTag(device, "friendlyName")),
773 m_manufacturer (upnpLib.Element_GetChildValueByTag(device, "manufacturer")),
774 m_manufacturerURL (upnpLib.Element_GetChildValueByTag(device, "manufacturerURL")),
775 m_modelDescription (upnpLib.Element_GetChildValueByTag(device, "modelDescription")),
776 m_modelName (upnpLib.Element_GetChildValueByTag(device, "modelName")),
777 m_modelNumber (upnpLib.Element_GetChildValueByTag(device, "modelNumber")),
778 m_modelURL (upnpLib.Element_GetChildValueByTag(device, "modelURL")),
779 m_serialNumber (upnpLib.Element_GetChildValueByTag(device, "serialNumber")),
780 m_UDN (upnpLib.Element_GetChildValueByTag(device, "UDN")),
781 m_UPC (upnpLib.Element_GetChildValueByTag(device, "UPC")),
782 m_presentationURL (upnpLib.Element_GetChildValueByTag(device, "presentationURL"))
784 std::ostringstream msg;
785 int presURLlen = strlen(URLBase.c_str()) +
786 strlen(m_presentationURL.c_str()) + 2;
787 std::vector<char> vpresURL(presURLlen);
788 char* presURL = &vpresURL[0];
789 int errcode = UpnpResolveURL(
790 URLBase.c_str(),
791 m_presentationURL.c_str(),
792 presURL);
793 if (errcode != UPNP_E_SUCCESS) {
794 msg << "Error generating presentationURL from " <<
795 "|" << URLBase << "|" <<
796 m_presentationURL << "|.";
797 AddDebugLogLineN(logUPnP, msg);
798 } else {
799 m_presentationURL = presURL;
802 msg.str("");
803 msg << "\n Device: " <<
804 "\n friendlyName: " << m_friendlyName <<
805 "\n deviceType: " << m_deviceType <<
806 "\n manufacturer: " << m_manufacturer <<
807 "\n manufacturerURL: " << m_manufacturerURL <<
808 "\n modelDescription: " << m_modelDescription <<
809 "\n modelName: " << m_modelName <<
810 "\n modelNumber: " << m_modelNumber <<
811 "\n modelURL: " << m_modelURL <<
812 "\n serialNumber: " << m_serialNumber <<
813 "\n UDN: " << m_UDN <<
814 "\n UPC: " << m_UPC <<
815 "\n presentationURL: " << m_presentationURL;
816 AddDebugLogLineN(logUPnP, msg);
820 CUPnPRootDevice::CUPnPRootDevice(
821 const CUPnPControlPoint &upnpControlPoint,
822 CUPnPLib &upnpLib,
823 IXML_Element *rootDevice,
824 const std::string &OriginalURLBase,
825 const std::string &FixedURLBase,
826 const char *location,
827 int expires)
829 CUPnPDevice(upnpControlPoint, upnpLib, rootDevice, FixedURLBase),
830 m_UPnPControlPoint(upnpControlPoint),
831 m_URLBase(OriginalURLBase),
832 m_location(location),
833 m_expires(expires)
835 std::ostringstream msg;
836 msg <<
837 "\n Root Device: " <<
838 "\n URLBase: " << m_URLBase <<
839 "\n Fixed URLBase: " << FixedURLBase <<
840 "\n location: " << m_location <<
841 "\n expires: " << m_expires;
842 AddDebugLogLineN(logUPnP, msg);
846 CUPnPControlPoint *CUPnPControlPoint::s_CtrlPoint = NULL;
849 CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort)
851 m_upnpLib(*this),
852 m_UPnPClientHandle(),
853 m_RootDeviceMap(),
854 m_ServiceMap(),
855 m_ActivePortMappingsMap(),
856 m_RootDeviceListMutex(),
857 m_IGWDeviceDetected(false),
858 m_WanService(NULL)
860 // Pointer to self
861 s_CtrlPoint = this;
862 // Null string at first
863 std::ostringstream msg;
865 // Start UPnP
866 int ret;
867 char *ipAddress = NULL;
868 unsigned short port = 0;
869 ret = UpnpInit(ipAddress, udpPort);
870 if (ret != UPNP_E_SUCCESS) {
871 msg << "error(UpnpInit): Error code ";
872 goto error;
874 port = UpnpGetServerPort();
875 ipAddress = UpnpGetServerIpAddress();
876 msg << "bound to " << ipAddress << ":" <<
877 port << ".";
878 AddDebugLogLineN(logUPnP, msg);
879 msg.str("");
880 ret = UpnpRegisterClient(
881 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
882 &m_UPnPClientHandle,
883 &m_UPnPClientHandle);
884 if (ret != UPNP_E_SUCCESS) {
885 msg << "error(UpnpRegisterClient): Error registering callback: ";
886 goto error;
889 // We could ask for just the right device here. If the root device
890 // contains the device we want, it will respond with the full XML doc,
891 // including the root device and every sub-device it has.
893 // But lets find out what we have in our network by calling UPNP_ROOT_DEVICE.
895 // We should not search twice, because this will produce two
896 // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
897 // on the mutex.
898 ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_ROOT_DEVICE.c_str(), NULL);
899 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this);
900 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this);
901 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this);
902 if (ret != UPNP_E_SUCCESS) {
903 msg << "error(UpnpSearchAsync): Error sending search request: ";
904 goto error;
907 // Wait for the UPnP initialization to complete.
909 // Lock the search timeout mutex
910 m_WaitForSearchTimeoutMutex.Lock();
912 // Lock it again, so that we block. Unlocking will only happen
913 // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
914 // callback.
915 CUPnPMutexLocker lock(m_WaitForSearchTimeoutMutex);
917 return;
919 // Error processing
920 error:
921 UpnpFinish();
922 msg << ret << ": " << m_upnpLib.GetUPnPErrorMessage(ret) << ".";
923 throw CUPnPException(msg);
927 CUPnPControlPoint::~CUPnPControlPoint()
929 for( RootDeviceMap::iterator it = m_RootDeviceMap.begin();
930 it != m_RootDeviceMap.end();
931 ++it) {
932 delete it->second;
934 // Remove all first
935 // RemoveAll();
936 UpnpUnRegisterClient(m_UPnPClientHandle);
937 UpnpFinish();
941 bool CUPnPControlPoint::AddPortMappings(
942 std::vector<CUPnPPortMapping> &upnpPortMapping)
944 std::ostringstream msg;
945 if (!WanServiceDetected()) {
946 msg << "UPnP Error: "
947 "CUPnPControlPoint::AddPortMapping: "
948 "WAN Service not detected.";
949 AddDebugLogLineC(logUPnP, msg);
950 return false;
953 int n = upnpPortMapping.size();
954 bool ok = false;
956 // Check the number of port mappings before
957 std::istringstream PortMappingNumberOfEntries(
958 m_WanService->GetStateVariable(
959 "PortMappingNumberOfEntries"));
960 unsigned long oldNumberOfEntries;
961 PortMappingNumberOfEntries >> oldNumberOfEntries;
963 // Add the enabled port mappings
964 for (int i = 0; i < n; ++i) {
965 if (upnpPortMapping[i].getEnabled() == "1") {
966 // Add the mapping to the control point
967 // active mappings list
968 m_ActivePortMappingsMap[upnpPortMapping[i].getKey()] =
969 upnpPortMapping[i];
971 // Add the port mapping
972 PrivateAddPortMapping(upnpPortMapping[i]);
976 // Test some variables, this is deprecated, might not work
977 // with some routers
978 m_WanService->GetStateVariable("ConnectionType");
979 m_WanService->GetStateVariable("PossibleConnectionTypes");
980 m_WanService->GetStateVariable("ConnectionStatus");
981 m_WanService->GetStateVariable("Uptime");
982 m_WanService->GetStateVariable("LastConnectionError");
983 m_WanService->GetStateVariable("RSIPAvailable");
984 m_WanService->GetStateVariable("NATEnabled");
985 m_WanService->GetStateVariable("ExternalIPAddress");
986 m_WanService->GetStateVariable("PortMappingNumberOfEntries");
987 m_WanService->GetStateVariable("PortMappingLeaseDuration");
989 // Just for testing
990 std::vector<CUPnPArgumentValue> argval;
991 argval.resize(0);
992 m_WanService->Execute("GetStatusInfo", argval);
994 #if 0
995 // These do not work. Their value must be requested for a
996 // specific port mapping.
997 m_WanService->GetStateVariable("PortMappingEnabled");
998 m_WanService->GetStateVariable("RemoteHost");
999 m_WanService->GetStateVariable("ExternalPort");
1000 m_WanService->GetStateVariable("InternalPort");
1001 m_WanService->GetStateVariable("PortMappingProtocol");
1002 m_WanService->GetStateVariable("InternalClient");
1003 m_WanService->GetStateVariable("PortMappingDescription");
1004 #endif
1006 // Debug only
1007 msg.str("");
1008 msg << "CUPnPControlPoint::DeletePortMappings: "
1009 "m_ActivePortMappingsMap.size() == " <<
1010 m_ActivePortMappingsMap.size();
1011 AddDebugLogLineN(logUPnP, msg);
1013 // Not very good, must find a better test
1014 PortMappingNumberOfEntries.str(
1015 m_WanService->GetStateVariable(
1016 "PortMappingNumberOfEntries"));
1017 unsigned long newNumberOfEntries;
1018 PortMappingNumberOfEntries >> newNumberOfEntries;
1019 ok = newNumberOfEntries - oldNumberOfEntries == 4;
1021 return ok;
1025 void CUPnPControlPoint::RefreshPortMappings()
1027 for ( PortMappingMap::iterator it = m_ActivePortMappingsMap.begin();
1028 it != m_ActivePortMappingsMap.end();
1029 ++it) {
1030 PrivateAddPortMapping(it->second);
1033 // For testing
1034 m_WanService->GetStateVariable("PortMappingNumberOfEntries");
1038 bool CUPnPControlPoint::PrivateAddPortMapping(
1039 CUPnPPortMapping &upnpPortMapping)
1041 // Get an IP address. The UPnP server one must do.
1042 std::string ipAddress(UpnpGetServerIpAddress());
1044 // Start building the action
1045 std::string actionName("AddPortMapping");
1046 std::vector<CUPnPArgumentValue> argval(8);
1048 // Action parameters
1049 argval[0].SetArgument("NewRemoteHost");
1050 argval[0].SetValue("");
1051 argval[1].SetArgument("NewExternalPort");
1052 argval[1].SetValue(upnpPortMapping.getPort());
1053 argval[2].SetArgument("NewProtocol");
1054 argval[2].SetValue(upnpPortMapping.getProtocol());
1055 argval[3].SetArgument("NewInternalPort");
1056 argval[3].SetValue(upnpPortMapping.getPort());
1057 argval[4].SetArgument("NewInternalClient");
1058 argval[4].SetValue(ipAddress);
1059 argval[5].SetArgument("NewEnabled");
1060 argval[5].SetValue("1");
1061 argval[6].SetArgument("NewPortMappingDescription");
1062 argval[6].SetValue(upnpPortMapping.getDescription());
1063 argval[7].SetArgument("NewLeaseDuration");
1064 argval[7].SetValue("0");
1066 // Execute
1067 bool ret = true;
1068 for (ServiceMap::iterator it = m_ServiceMap.begin();
1069 it != m_ServiceMap.end(); ++it) {
1070 ret &= it->second->Execute(actionName, argval);
1073 return ret;
1077 bool CUPnPControlPoint::DeletePortMappings(
1078 std::vector<CUPnPPortMapping> &upnpPortMapping)
1080 std::ostringstream msg;
1081 if (!WanServiceDetected()) {
1082 msg << "UPnP Error: "
1083 "CUPnPControlPoint::DeletePortMapping: "
1084 "WAN Service not detected.";
1085 AddDebugLogLineC(logUPnP, msg);
1086 return false;
1089 int n = upnpPortMapping.size();
1090 bool ok = false;
1092 // Check the number of port mappings before
1093 std::istringstream PortMappingNumberOfEntries(
1094 m_WanService->GetStateVariable(
1095 "PortMappingNumberOfEntries"));
1096 unsigned long oldNumberOfEntries;
1097 PortMappingNumberOfEntries >> oldNumberOfEntries;
1099 // Delete the enabled port mappings
1100 for (int i = 0; i < n; ++i) {
1101 if (upnpPortMapping[i].getEnabled() == "1") {
1102 // Delete the mapping from the control point
1103 // active mappings list
1104 PortMappingMap::iterator it =
1105 m_ActivePortMappingsMap.find(
1106 upnpPortMapping[i].getKey());
1107 if (it != m_ActivePortMappingsMap.end()) {
1108 m_ActivePortMappingsMap.erase(it);
1109 } else {
1110 msg << "UPnP Error: "
1111 "CUPnPControlPoint::DeletePortMapping: "
1112 "Mapping was not found in the active "
1113 "mapping map.";
1114 AddDebugLogLineC(logUPnP, msg);
1117 // Delete the port mapping
1118 PrivateDeletePortMapping(upnpPortMapping[i]);
1122 // Debug only
1123 msg.str("");
1124 msg << "CUPnPControlPoint::DeletePortMappings: "
1125 "m_ActivePortMappingsMap.size() == " <<
1126 m_ActivePortMappingsMap.size();
1127 AddDebugLogLineN(logUPnP, msg);
1129 // Not very good, must find a better test
1130 PortMappingNumberOfEntries.str(
1131 m_WanService->GetStateVariable(
1132 "PortMappingNumberOfEntries"));
1133 unsigned long newNumberOfEntries;
1134 PortMappingNumberOfEntries >> newNumberOfEntries;
1135 ok = oldNumberOfEntries - newNumberOfEntries == 4;
1137 return ok;
1141 bool CUPnPControlPoint::PrivateDeletePortMapping(
1142 CUPnPPortMapping &upnpPortMapping)
1144 // Start building the action
1145 std::string actionName("DeletePortMapping");
1146 std::vector<CUPnPArgumentValue> argval(3);
1148 // Action parameters
1149 argval[0].SetArgument("NewRemoteHost");
1150 argval[0].SetValue("");
1151 argval[1].SetArgument("NewExternalPort");
1152 argval[1].SetValue(upnpPortMapping.getPort());
1153 argval[2].SetArgument("NewProtocol");
1154 argval[2].SetValue(upnpPortMapping.getProtocol());
1156 // Execute
1157 bool ret = true;
1158 for (ServiceMap::iterator it = m_ServiceMap.begin();
1159 it != m_ServiceMap.end(); ++it) {
1160 ret &= it->second->Execute(actionName, argval);
1163 return ret;
1167 // This function is static
1168 int CUPnPControlPoint::Callback(Upnp_EventType EventType, void *Event, void * /*Cookie*/)
1170 std::ostringstream msg;
1171 std::ostringstream msg2;
1172 // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
1173 // happen with a wrong cookie and... boom!
1174 // CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
1175 CUPnPControlPoint *upnpCP = CUPnPControlPoint::s_CtrlPoint;
1177 //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
1178 switch (EventType) {
1179 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
1180 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
1181 msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): ";
1182 msg2<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
1183 goto upnpDiscovery;
1184 case UPNP_DISCOVERY_SEARCH_RESULT: {
1185 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n");
1186 msg << "error(UPNP_DISCOVERY_SEARCH_RESULT): ";
1187 msg2<< "UPNP_DISCOVERY_SEARCH_RESULT: ";
1188 // UPnP Discovery
1189 upnpDiscovery:
1190 struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
1191 IXML_Document *doc = NULL;
1192 int ret;
1193 if (d_event->ErrCode != UPNP_E_SUCCESS) {
1194 msg << upnpCP->m_upnpLib.GetUPnPErrorMessage(d_event->ErrCode) << ".";
1195 AddDebugLogLineC(logUPnP, msg);
1197 // Get the XML tree device description in doc
1198 ret = UpnpDownloadXmlDoc(d_event->Location, &doc);
1199 if (ret != UPNP_E_SUCCESS) {
1200 msg << "Error retrieving device description from " <<
1201 d_event->Location << ": " <<
1202 upnpCP->m_upnpLib.GetUPnPErrorMessage(ret) <<
1203 "(" << ret << ").";
1204 AddDebugLogLineC(logUPnP, msg);
1205 } else {
1206 msg2 << "Retrieving device description from " <<
1207 d_event->Location << ".";
1208 AddDebugLogLineN(logUPnP, msg2);
1210 if (doc) {
1211 // Get the root node
1212 IXML_Element *root =
1213 upnpCP->m_upnpLib.Element_GetRootElement(doc);
1214 // Extract the URLBase
1215 const std::string urlBase = upnpCP->m_upnpLib.
1216 Element_GetChildValueByTag(root, "URLBase");
1217 // Get the root device
1218 IXML_Element *rootDevice = upnpCP->m_upnpLib.
1219 Element_GetFirstChildByTag(root, "device");
1220 // Extract the deviceType
1221 std::string devType(upnpCP->m_upnpLib.
1222 Element_GetChildValueByTag(rootDevice, "deviceType"));
1223 // Only add device if it is an InternetGatewayDevice
1224 if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) {
1225 // This condition can be used to auto-detect
1226 // the UPnP device we are interested in.
1227 // Obs.: Don't block the entry here on this
1228 // condition! There may be more than one device,
1229 // and the first that enters may not be the one
1230 // we are interested in!
1231 upnpCP->SetIGWDeviceDetected(true);
1232 // Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
1233 // we don't want to spam our logs.
1234 if (EventType != UPNP_DISCOVERY_ADVERTISEMENT_ALIVE) {
1235 msg.str("Internet Gateway Device Detected.");
1236 AddDebugLogLineC(logUPnP, msg);
1238 // Add the root device to our list
1239 upnpCP->AddRootDevice(rootDevice, urlBase,
1240 d_event->Location, d_event->Expires);
1242 // Free the XML doc tree
1243 ixmlDocument_free(doc);
1245 break;
1247 case UPNP_DISCOVERY_SEARCH_TIMEOUT: {
1248 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1249 // Search timeout
1250 msg << "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1251 AddDebugLogLineN(logUPnP, msg);
1253 // Unlock the search timeout mutex
1254 upnpCP->m_WaitForSearchTimeoutMutex.Unlock();
1256 break;
1258 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: {
1259 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
1260 // UPnP Device Removed
1261 struct Upnp_Discovery *dab_event = (struct Upnp_Discovery *)Event;
1262 if (dab_event->ErrCode != UPNP_E_SUCCESS) {
1263 msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
1264 upnpCP->m_upnpLib.GetUPnPErrorMessage(dab_event->ErrCode) <<
1265 ".";
1266 AddDebugLogLineC(logUPnP, msg);
1268 std::string devType = dab_event->DeviceType;
1269 // Check for an InternetGatewayDevice and removes it from the list
1270 std::transform(devType.begin(), devType.end(), devType.begin(), tolower);
1271 if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) {
1272 upnpCP->RemoveRootDevice(dab_event->DeviceId);
1274 break;
1276 case UPNP_EVENT_RECEIVED: {
1277 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1278 // Event reveived
1279 struct Upnp_Event *e_event = (struct Upnp_Event *)Event;
1280 const std::string Sid = e_event->Sid;
1281 // Parses the event
1282 upnpCP->OnEventReceived(Sid, e_event->EventKey, e_event->ChangedVariables);
1283 break;
1285 case UPNP_EVENT_SUBSCRIBE_COMPLETE:
1286 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
1287 msg << "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): ";
1288 goto upnpEventRenewalComplete;
1289 case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
1290 //fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
1291 msg << "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): ";
1292 goto upnpEventRenewalComplete;
1293 case UPNP_EVENT_RENEWAL_COMPLETE: {
1294 //fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n");
1295 msg << "error(UPNP_EVENT_RENEWAL_COMPLETE): ";
1296 upnpEventRenewalComplete:
1297 struct Upnp_Event_Subscribe *es_event =
1298 (struct Upnp_Event_Subscribe *)Event;
1299 if (es_event->ErrCode != UPNP_E_SUCCESS) {
1300 msg << "Error in Event Subscribe Callback";
1301 upnpCP->m_upnpLib.processUPnPErrorMessage(
1302 msg.str(), es_event->ErrCode, NULL, NULL);
1303 } else {
1304 #if 0
1305 TvCtrlPointHandleSubscribeUpdate(
1306 GET_UPNP_STRING(es_event->PublisherUrl),
1307 es_event->Sid,
1308 es_event->TimeOut );
1309 #endif
1312 break;
1315 case UPNP_EVENT_AUTORENEWAL_FAILED:
1316 //fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n");
1317 msg << "error(UPNP_EVENT_AUTORENEWAL_FAILED): ";
1318 msg2 << "UPNP_EVENT_AUTORENEWAL_FAILED: ";
1319 goto upnpEventSubscriptionExpired;
1320 case UPNP_EVENT_SUBSCRIPTION_EXPIRED: {
1321 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
1322 msg << "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): ";
1323 msg2 << "UPNP_EVENT_SUBSCRIPTION_EXPIRED: ";
1324 upnpEventSubscriptionExpired:
1325 struct Upnp_Event_Subscribe *es_event =
1326 (struct Upnp_Event_Subscribe *)Event;
1327 Upnp_SID newSID;
1328 int TimeOut = 1801;
1329 int ret = UpnpSubscribe(
1330 upnpCP->m_UPnPClientHandle,
1331 GET_UPNP_STRING(es_event->PublisherUrl),
1332 &TimeOut,
1333 newSID);
1334 if (ret != UPNP_E_SUCCESS) {
1335 msg << "Error Subscribing to EventURL";
1336 upnpCP->m_upnpLib.processUPnPErrorMessage(
1337 msg.str(), es_event->ErrCode, NULL, NULL);
1338 } else {
1339 ServiceMap::iterator it =
1340 upnpCP->m_ServiceMap.find(GET_UPNP_STRING(es_event->PublisherUrl));
1341 if (it != upnpCP->m_ServiceMap.end()) {
1342 CUPnPService &service = *(it->second);
1343 service.SetTimeout(TimeOut);
1344 service.SetSID(newSID);
1345 msg2 << "Re-subscribed to EventURL '" <<
1346 GET_UPNP_STRING(es_event->PublisherUrl) <<
1347 "' with SID == '" <<
1348 newSID << "'.";
1349 AddDebugLogLineC(logUPnP, msg2);
1350 // In principle, we should test to see if the
1351 // service is the same. But here we only have one
1352 // service, so...
1353 upnpCP->RefreshPortMappings();
1354 } else {
1355 msg << "Error: did not find service " <<
1356 newSID << " in the service map.";
1357 AddDebugLogLineC(logUPnP, msg);
1360 break;
1362 case UPNP_CONTROL_ACTION_COMPLETE: {
1363 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n");
1364 // This is here if we choose to do this asynchronously
1365 struct Upnp_Action_Complete *a_event =
1366 (struct Upnp_Action_Complete *)Event;
1367 if (a_event->ErrCode != UPNP_E_SUCCESS) {
1368 upnpCP->m_upnpLib.processUPnPErrorMessage(
1369 "UpnpSendActionAsync",
1370 a_event->ErrCode, NULL,
1371 a_event->ActionResult);
1372 } else {
1373 // Check the response document
1374 upnpCP->m_upnpLib.ProcessActionResponse(
1375 a_event->ActionResult,
1376 "<UpnpSendActionAsync>");
1378 /* No need for any processing here, just print out results.
1379 * Service state table updates are handled by events.
1381 break;
1383 case UPNP_CONTROL_GET_VAR_COMPLETE: {
1384 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n");
1385 msg << "error(UPNP_CONTROL_GET_VAR_COMPLETE): ";
1386 struct Upnp_State_Var_Complete *sv_event =
1387 (struct Upnp_State_Var_Complete *)Event;
1388 if (sv_event->ErrCode != UPNP_E_SUCCESS) {
1389 msg << "m_UpnpGetServiceVarStatusAsync";
1390 upnpCP->m_upnpLib.processUPnPErrorMessage(
1391 msg.str(), sv_event->ErrCode, NULL, NULL);
1392 } else {
1393 #if 0
1394 // Warning: The use of UpnpGetServiceVarStatus and
1395 // UpnpGetServiceVarStatusAsync is deprecated by the
1396 // UPnP forum.
1397 TvCtrlPointHandleGetVar(
1398 sv_event->CtrlUrl,
1399 sv_event->StateVarName,
1400 sv_event->CurrentVal );
1401 #endif
1403 break;
1405 // ignore these cases, since this is not a device
1406 case UPNP_CONTROL_GET_VAR_REQUEST:
1407 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n");
1408 msg << "error(UPNP_CONTROL_GET_VAR_REQUEST): ";
1409 goto eventSubscriptionRequest;
1410 case UPNP_CONTROL_ACTION_REQUEST:
1411 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n");
1412 msg << "error(UPNP_CONTROL_ACTION_REQUEST): ";
1413 goto eventSubscriptionRequest;
1414 case UPNP_EVENT_SUBSCRIPTION_REQUEST:
1415 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
1416 msg << "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): ";
1417 eventSubscriptionRequest:
1418 msg << "This is not a UPnP Device, this is a UPnP Control Point, event ignored.";
1419 AddDebugLogLineC(logUPnP, msg);
1420 break;
1421 default:
1422 // Humm, this is not good, we forgot to handle something...
1423 fprintf(stderr,
1424 "Callback: default... Unknown event:'%d', not good.\n",
1425 EventType);
1426 msg << "error(UPnP::Callback): Event not handled:'" <<
1427 EventType << "'.";
1428 fprintf(stderr, "%s\n", msg.str().c_str());
1429 AddDebugLogLineC(logUPnP, msg);
1430 // Better not throw in the callback. Who would catch it?
1431 //throw CUPnPException(msg);
1432 break;
1435 return 0;
1439 void CUPnPControlPoint::OnEventReceived(
1440 const std::string &Sid,
1441 int EventKey,
1442 IXML_Document *ChangedVariablesDoc)
1444 std::ostringstream msg;
1445 msg << "UPNP_EVENT_RECEIVED:" <<
1446 "\n SID: " << Sid <<
1447 "\n Key: " << EventKey <<
1448 "\n Property list:";
1449 IXML_Element *root =
1450 m_upnpLib.Element_GetRootElement(ChangedVariablesDoc);
1451 IXML_Element *child =
1452 m_upnpLib.Element_GetFirstChild(root);
1453 if (child) {
1454 while (child) {
1455 IXML_Element *child2 =
1456 m_upnpLib.Element_GetFirstChild(child);
1457 const DOMString childTag =
1458 m_upnpLib.Element_GetTag(child2);
1459 std::string childValue =
1460 m_upnpLib.Element_GetTextValue(child2);
1461 msg << "\n " <<
1462 childTag << "='" <<
1463 childValue << "'";
1464 child = m_upnpLib.Element_GetNextSibling(child);
1466 } else {
1467 msg << "\n Empty property list.";
1469 AddDebugLogLineC(logUPnP, msg);
1470 // Freeing that doc segfaults. Probably should not be freed.
1471 //ixmlDocument_free(ChangedVariablesDoc);
1475 void CUPnPControlPoint::AddRootDevice(
1476 IXML_Element *rootDevice, const std::string &urlBase,
1477 const char *location, int expires)
1479 // Lock the Root Device List
1480 CUPnPMutexLocker lock(m_RootDeviceListMutex);
1482 // Root node's URLBase
1483 std::string OriginalURLBase(urlBase);
1484 std::string FixedURLBase(OriginalURLBase.empty() ?
1485 location :
1486 OriginalURLBase);
1488 // Get the UDN (Unique Device Name)
1489 std::string UDN(
1490 m_upnpLib.Element_GetChildValueByTag(rootDevice, "UDN"));
1491 RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
1492 bool alreadyAdded = it != m_RootDeviceMap.end();
1493 if (alreadyAdded) {
1494 // Just set the expires field
1495 it->second->SetExpires(expires);
1496 } else {
1497 // Add a new root device to the root device list
1498 CUPnPRootDevice *upnpRootDevice = new CUPnPRootDevice(
1499 *this, m_upnpLib, rootDevice,
1500 OriginalURLBase, FixedURLBase,
1501 location, expires);
1502 m_RootDeviceMap[upnpRootDevice->GetUDN()] = upnpRootDevice;
1507 void CUPnPControlPoint::RemoveRootDevice(const char *udn)
1509 // Lock the Root Device List
1510 CUPnPMutexLocker lock(m_RootDeviceListMutex);
1512 // Remove
1513 std::string UDN(udn);
1514 RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
1515 if (it != m_RootDeviceMap.end()) {
1516 delete it->second;
1517 m_RootDeviceMap.erase(UDN);
1522 void CUPnPControlPoint::Subscribe(CUPnPService &service)
1524 std::ostringstream msg;
1526 IXML_Document *scpdDoc = NULL;
1527 int errcode = UpnpDownloadXmlDoc(
1528 service.GetAbsSCPDURL().c_str(), &scpdDoc);
1529 if (errcode == UPNP_E_SUCCESS) {
1530 // Get the root node of this service (the SCPD Document)
1531 IXML_Element *scpdRoot =
1532 m_upnpLib.Element_GetRootElement(scpdDoc);
1533 CUPnPSCPD *scpd = new CUPnPSCPD(*this, m_upnpLib,
1534 scpdRoot, service.GetAbsSCPDURL());
1535 service.SetSCPD(scpd);
1536 m_ServiceMap[service.GetAbsEventSubURL()] = &service;
1537 msg << "Successfully retrieved SCPD Document for service " <<
1538 service.GetServiceType() << ", absEventSubURL: " <<
1539 service.GetAbsEventSubURL() << ".";
1540 AddDebugLogLineC(logUPnP, msg);
1541 msg.str("");
1543 // Now try to subscribe to this service. If the subscription
1544 // is not successfull, we will not be notified about events,
1545 // but it may be possible to use the service anyway.
1546 errcode = UpnpSubscribe(m_UPnPClientHandle,
1547 service.GetAbsEventSubURL().c_str(),
1548 service.GetTimeoutAddr(),
1549 service.GetSID());
1550 if (errcode == UPNP_E_SUCCESS) {
1551 msg << "Successfully subscribed to service " <<
1552 service.GetServiceType() << ", absEventSubURL: " <<
1553 service.GetAbsEventSubURL() << ".";
1554 AddDebugLogLineC(logUPnP, msg);
1555 } else {
1556 msg << "Error subscribing to service " <<
1557 service.GetServiceType() << ", absEventSubURL: " <<
1558 service.GetAbsEventSubURL() << ", error: " <<
1559 m_upnpLib.GetUPnPErrorMessage(errcode) << ".";
1560 goto error;
1562 } else {
1563 msg << "Error getting SCPD Document from " <<
1564 service.GetAbsSCPDURL() << ".";
1565 AddDebugLogLineC(logUPnP, msg);
1568 return;
1570 // Error processing
1571 error:
1572 AddDebugLogLineC(logUPnP, msg);
1576 void CUPnPControlPoint::Unsubscribe(CUPnPService &service)
1578 ServiceMap::iterator it = m_ServiceMap.find(service.GetAbsEventSubURL());
1579 if (it != m_ServiceMap.end()) {
1580 m_ServiceMap.erase(it);
1581 UpnpUnSubscribe(m_UPnPClientHandle, service.GetSID());
1585 #endif /* ENABLE_UPNP */