2 // This file is part of the aMule Project.
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 )
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
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
27 # include "config.h" // Needed for ENABLE_UPNP
32 // check for broken Debian-hacked libUPnP
34 #ifdef STRING_H // defined in UpnpString.h Yes, I would have liked UPNPSTRING_H much better.
35 #define BROKEN_DEBIAN_LIBUPNP
40 #include <algorithm> // For transform()
42 #ifdef BROKEN_DEBIAN_LIBUPNP
43 #define GET_UPNP_STRING(a) UpnpString_get_String(a)
45 #define GET_UPNP_STRING(a) (a)
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";
64 * Case insensitive std::string comparison
66 bool stdStringIsEqualCI(const std::string
&s1
, const std::string
&s2
)
70 std::transform(ns1
.begin(), ns1
.end(), ns1
.begin(), tolower
);
71 std::transform(ns2
.begin(), ns2
.end(), ns2
.begin(), tolower
);
76 CUPnPPortMapping::CUPnPPortMapping(
78 const std::string
&protocol
,
80 const std::string
&description
)
84 m_enabled(enabled
? "1" : "0"),
85 m_description(description
),
88 std::ostringstream oss
;
91 m_key
= m_protocol
+ m_port
;
95 const std::string
&CUPnPLib::UPNP_ROOT_DEVICE
=
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
,
133 const DOMString errorString
,
134 IXML_Document
*doc
) const
136 std::ostringstream msg
;
137 if (errorString
== NULL
|| *errorString
== 0) {
138 errorString
= "Not available";
145 CUPnPError
e(*this, doc
);
146 msg
<< e
.getErrorCode() <<
147 "', Error description :'" <<
148 e
.getErrorDescription() <<
152 "', Error description :'" <<
156 AddDebugLogLineN(logUPnP
, msg
);
160 ": UPnP SDK error: " <<
161 GetUPnPErrorMessage(errorCode
) <<
162 " (" << errorCode
<< ").";
163 AddDebugLogLineN(logUPnP
, msg
);
170 void CUPnPLib::ProcessActionResponse(
171 IXML_Document
*RespDoc
,
172 const std::string
&actionName
) const
174 std::ostringstream msg
;
176 IXML_Element
*root
= Element_GetRootElement(RespDoc
);
177 IXML_Element
*child
= Element_GetFirstChild(root
);
180 const DOMString childTag
= Element_GetTag(child
);
181 std::string childValue
= Element_GetTextValue(child
);
185 child
= Element_GetNextSibling(child
);
188 msg
<< "\n Empty response for action '" <<
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
));
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
);
245 * \brief Returns the TEXT node value of the current node.
247 const std::string
CUPnPLib::Element_GetTextValue(
248 IXML_Element
*element
) const
251 return stdEmptyString
;
253 IXML_Node
*text
= ixmlNode_getFirstChild(&element
->n
);
254 const DOMString s
= ixmlNode_getNodeValue(text
);
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
282 IXML_Element
*CUPnPLib::Element_GetFirstChildByTag(
283 IXML_Element
*element
,
284 const DOMString tag
) const
286 if (!element
|| !tag
) {
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
) {
312 IXML_Node
*child
= &element
->n
;
313 const DOMString childTag
= NULL
;
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
);
333 ixmlNamedNodeMap_free(NamedNodeMap
);
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
,
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
,
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
,
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
,
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
,
432 const std::string
&SCPDURL
)
434 m_UPnPControlPoint(upnpControlPoint
),
435 m_ActionList(upnpControlPoint
, upnpLib
, scpd
, SCPDURL
),
436 m_ServiceStateTable(upnpControlPoint
, upnpLib
, scpd
, SCPDURL
),
442 CUPnPArgumentValue::CUPnPArgumentValue()
450 CUPnPArgumentValue::CUPnPArgumentValue(
451 const std::string
&argument
, const std::string
&value
)
453 m_argument(argument
),
459 CUPnPService::CUPnPService(
460 const CUPnPControlPoint
&upnpControlPoint
,
462 IXML_Element
*service
,
463 const std::string
&URLBase
)
465 m_UPnPControlPoint(upnpControlPoint
),
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")),
475 std::ostringstream msg
;
478 std::vector
<char> vscpdURL(URLBase
.length() + m_SCPDURL
.length() + 1);
479 char *scpdURL
= &vscpdURL
[0];
480 errcode
= UpnpResolveURL(
484 if( errcode
!= UPNP_E_SUCCESS
) {
485 msg
<< "Error generating scpdURL from " <<
486 "|" << URLBase
<< "|" <<
488 AddDebugLogLineN(logUPnP
, msg
);
490 m_absSCPDURL
= scpdURL
;
493 std::vector
<char> vcontrolURL(
494 URLBase
.length() + m_controlURL
.length() + 1);
495 char *controlURL
= &vcontrolURL
[0];
496 errcode
= UpnpResolveURL(
498 m_controlURL
.c_str(),
500 if( errcode
!= UPNP_E_SUCCESS
) {
501 msg
<< "Error generating controlURL from " <<
502 "|" << URLBase
<< "|" <<
503 m_controlURL
<< "|.";
504 AddDebugLogLineN(logUPnP
, msg
);
506 m_absControlURL
= controlURL
;
509 std::vector
<char> veventURL(
510 URLBase
.length() + m_eventSubURL
.length() + 1);
511 char *eventURL
= &veventURL
[0];
512 errcode
= UpnpResolveURL(
514 m_eventSubURL
.c_str(),
516 if( errcode
!= UPNP_E_SUCCESS
) {
517 msg
<< "Error generating eventURL from " <<
518 "|" << URLBase
<< "|" <<
519 m_eventSubURL
<< "|.";
520 AddDebugLogLineN(logUPnP
, msg
);
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
) {
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
) {
544 //#warning Delete this code on release.
545 if (!upnpLib
.m_ctrlPoint
.WanServiceDetected()) {
546 // This condition can be used to suspend the parse
549 //#warning Delete this code when m_WanService is no longer used.
550 upnpLib
.m_ctrlPoint
.SetWanService(this);
553 msg
<< "WAN Service Detected: '" <<
554 m_serviceType
<< "'.";
555 AddDebugLogLineC(logUPnP
, msg
);
557 upnpLib
.m_ctrlPoint
.Subscribe(*this);
559 //#warning Delete this code on release.
562 msg
<< "WAN service detected again: '" <<
564 "'. Will only use the first instance.";
565 AddDebugLogLineC(logUPnP
, msg
);
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
);
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
);
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
);
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
);
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
);
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
);
661 ArgValue
[i
].GetArgument() <<
663 ArgValue
[i
].GetValue() <<
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(
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
);
685 ActionDoc
= UpnpMakeAction(
686 action
.GetName().c_str(),
687 GetServiceType().c_str(),
690 msg
<< "Error: UpnpMakeAction returned NULL.";
691 AddDebugLogLineN(logUPnP
, msg
);
696 // Send the action asynchronously
698 m_UPnPControlPoint
.GetUPnPClientHandle(),
699 GetAbsControlURL().c_str(),
700 GetServiceType().c_str(),
702 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
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
);
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
);
734 const std::string
CUPnPService::GetStateVariable(
735 const std::string
&stateVariableName
) const
737 std::ostringstream msg
;
739 int ret
= UpnpGetServiceVarStatus(
740 m_UPnPControlPoint
.GetUPnPClientHandle(),
741 GetAbsControlURL().c_str(),
742 stateVariableName
.c_str(),
744 if (ret
!= UPNP_E_SUCCESS
) {
745 msg
<< "GetStateVariable(\"" <<
747 "\"): in a call to UpnpGetServiceVarStatus";
748 m_upnpLib
.processUPnPErrorMessage(
749 msg
.str(), ret
, StVarVal
, NULL
);
750 return stdEmptyString
;
752 msg
<< "GetStateVariable: " <<
757 AddDebugLogLineN(logUPnP
, msg
);
758 return stdEmptyString
;
762 CUPnPDevice::CUPnPDevice(
763 const CUPnPControlPoint
&upnpControlPoint
,
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(
791 m_presentationURL
.c_str(),
793 if (errcode
!= UPNP_E_SUCCESS
) {
794 msg
<< "Error generating presentationURL from " <<
795 "|" << URLBase
<< "|" <<
796 m_presentationURL
<< "|.";
797 AddDebugLogLineN(logUPnP
, msg
);
799 m_presentationURL
= presURL
;
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
,
823 IXML_Element
*rootDevice
,
824 const std::string
&OriginalURLBase
,
825 const std::string
&FixedURLBase
,
826 const char *location
,
829 CUPnPDevice(upnpControlPoint
, upnpLib
, rootDevice
, FixedURLBase
),
830 m_UPnPControlPoint(upnpControlPoint
),
831 m_URLBase(OriginalURLBase
),
832 m_location(location
),
835 std::ostringstream 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
)
852 m_UPnPClientHandle(),
855 m_ActivePortMappingsMap(),
856 m_RootDeviceListMutex(),
857 m_IGWDeviceDetected(false),
862 // Null string at first
863 std::ostringstream msg
;
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 ";
874 port
= UpnpGetServerPort();
875 ipAddress
= UpnpGetServerIpAddress();
876 msg
<< "bound to " << ipAddress
<< ":" <<
878 AddDebugLogLineN(logUPnP
, msg
);
880 ret
= UpnpRegisterClient(
881 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
883 &m_UPnPClientHandle
);
884 if (ret
!= UPNP_E_SUCCESS
) {
885 msg
<< "error(UpnpRegisterClient): Error registering callback: ";
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
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: ";
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
915 CUPnPMutexLocker
lock(m_WaitForSearchTimeoutMutex
);
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();
936 UpnpUnRegisterClient(m_UPnPClientHandle
);
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
);
953 int n
= upnpPortMapping
.size();
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()] =
971 // Add the port mapping
972 PrivateAddPortMapping(upnpPortMapping
[i
]);
976 // Test some variables, this is deprecated, might not work
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");
990 std::vector
<CUPnPArgumentValue
> argval
;
992 m_WanService
->Execute("GetStatusInfo", argval
);
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");
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;
1025 void CUPnPControlPoint::RefreshPortMappings()
1027 for ( PortMappingMap::iterator it
= m_ActivePortMappingsMap
.begin();
1028 it
!= m_ActivePortMappingsMap
.end();
1030 PrivateAddPortMapping(it
->second
);
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");
1068 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1069 it
!= m_ServiceMap
.end(); ++it
) {
1070 ret
&= it
->second
->Execute(actionName
, argval
);
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
);
1089 int n
= upnpPortMapping
.size();
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
);
1110 msg
<< "UPnP Error: "
1111 "CUPnPControlPoint::DeletePortMapping: "
1112 "Mapping was not found in the active "
1114 AddDebugLogLineC(logUPnP
, msg
);
1117 // Delete the port mapping
1118 PrivateDeletePortMapping(upnpPortMapping
[i
]);
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;
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());
1158 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1159 it
!= m_ServiceMap
.end(); ++it
) {
1160 ret
&= it
->second
->Execute(actionName
, argval
);
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: ";
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: ";
1190 struct Upnp_Discovery
*d_event
= (struct Upnp_Discovery
*)Event
;
1191 IXML_Document
*doc
= NULL
;
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
) <<
1204 AddDebugLogLineC(logUPnP
, msg
);
1206 msg2
<< "Retrieving device description from " <<
1207 d_event
->Location
<< ".";
1208 AddDebugLogLineN(logUPnP
, msg2
);
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
);
1247 case UPNP_DISCOVERY_SEARCH_TIMEOUT
: {
1248 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1250 msg
<< "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1251 AddDebugLogLineN(logUPnP
, msg
);
1253 // Unlock the search timeout mutex
1254 upnpCP
->m_WaitForSearchTimeoutMutex
.Unlock();
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
) <<
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
);
1276 case UPNP_EVENT_RECEIVED
: {
1277 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1279 struct Upnp_Event
*e_event
= (struct Upnp_Event
*)Event
;
1280 const std::string Sid
= e_event
->Sid
;
1282 upnpCP
->OnEventReceived(Sid
, e_event
->EventKey
, e_event
->ChangedVariables
);
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
);
1305 TvCtrlPointHandleSubscribeUpdate(
1306 GET_UPNP_STRING(es_event
->PublisherUrl
),
1308 es_event
->TimeOut
);
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
;
1329 int ret
= UpnpSubscribe(
1330 upnpCP
->m_UPnPClientHandle
,
1331 GET_UPNP_STRING(es_event
->PublisherUrl
),
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
);
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 == '" <<
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
1353 upnpCP
->RefreshPortMappings();
1355 msg
<< "Error: did not find service " <<
1356 newSID
<< " in the service map.";
1357 AddDebugLogLineC(logUPnP
, msg
);
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
);
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.
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
);
1394 // Warning: The use of UpnpGetServiceVarStatus and
1395 // UpnpGetServiceVarStatusAsync is deprecated by the
1397 TvCtrlPointHandleGetVar(
1399 sv_event
->StateVarName
,
1400 sv_event
->CurrentVal
);
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
);
1422 // Humm, this is not good, we forgot to handle something...
1424 "Callback: default... Unknown event:'%d', not good.\n",
1426 msg
<< "error(UPnP::Callback): Event not handled:'" <<
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);
1439 void CUPnPControlPoint::OnEventReceived(
1440 const std::string
&Sid
,
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
);
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
);
1464 child
= m_upnpLib
.Element_GetNextSibling(child
);
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() ?
1488 // Get the UDN (Unique Device Name)
1490 m_upnpLib
.Element_GetChildValueByTag(rootDevice
, "UDN"));
1491 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1492 bool alreadyAdded
= it
!= m_RootDeviceMap
.end();
1494 // Just set the expires field
1495 it
->second
->SetExpires(expires
);
1497 // Add a new root device to the root device list
1498 CUPnPRootDevice
*upnpRootDevice
= new CUPnPRootDevice(
1499 *this, m_upnpLib
, rootDevice
,
1500 OriginalURLBase
, FixedURLBase
,
1502 m_RootDeviceMap
[upnpRootDevice
->GetUDN()] = upnpRootDevice
;
1507 void CUPnPControlPoint::RemoveRootDevice(const char *udn
)
1509 // Lock the Root Device List
1510 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1513 std::string
UDN(udn
);
1514 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1515 if (it
!= m_RootDeviceMap
.end()) {
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
);
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(),
1550 if (errcode
== UPNP_E_SUCCESS
) {
1551 msg
<< "Successfully subscribed to service " <<
1552 service
.GetServiceType() << ", absEventSubURL: " <<
1553 service
.GetAbsEventSubURL() << ".";
1554 AddDebugLogLineC(logUPnP
, msg
);
1556 msg
<< "Error subscribing to service " <<
1557 service
.GetServiceType() << ", absEventSubURL: " <<
1558 service
.GetAbsEventSubURL() << ", error: " <<
1559 m_upnpLib
.GetUPnPErrorMessage(errcode
) << ".";
1563 msg
<< "Error getting SCPD Document from " <<
1564 service
.GetAbsSCPDURL() << ".";
1565 AddDebugLogLineC(logUPnP
, msg
);
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 */