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
34 #include <algorithm> // For transform()
37 std::string stdEmptyString
;
39 const char s_argument
[] = "argument";
40 const char s_argumentList
[] = "argumentList";
41 const char s_action
[] = "action";
42 const char s_actionList
[] = "actionList";
43 const char s_allowedValue
[] = "allowedValue";
44 const char s_allowedValueList
[] = "allowedValueList";
45 const char s_stateVariable
[] = "stateVariable";
46 const char s_serviceStateTable
[] = "serviceStateTable";
47 const char s_service
[] = "service";
48 const char s_serviceList
[] = "serviceList";
49 const char s_device
[] = "device";
50 const char s_deviceList
[] = "deviceList";
53 * Case insensitive std::string comparison
55 bool stdStringIsEqualCI(const std::string
&s1
, const std::string
&s2
)
59 std::transform(ns1
.begin(), ns1
.end(), ns1
.begin(), tolower
);
60 std::transform(ns2
.begin(), ns2
.end(), ns2
.begin(), tolower
);
65 CUPnPPortMapping::CUPnPPortMapping(
67 const std::string
&protocol
,
69 const std::string
&description
)
73 m_enabled(enabled
? "1" : "0"),
74 m_description(description
),
77 std::ostringstream oss
;
80 m_key
= m_protocol
+ m_port
;
84 const std::string
&CUPnPLib::UPNP_ROOT_DEVICE
=
87 const std::string
&CUPnPLib::UPNP_DEVICE_IGW
=
88 "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
89 const std::string
&CUPnPLib::UPNP_DEVICE_WAN
=
90 "urn:schemas-upnp-org:device:WANDevice:1";
91 const std::string
&CUPnPLib::UPNP_DEVICE_WAN_CONNECTION
=
92 "urn:schemas-upnp-org:device:WANConnectionDevice:1";
93 const std::string
&CUPnPLib::UPNP_DEVICE_LAN
=
94 "urn:schemas-upnp-org:device:LANDevice:1";
96 const std::string
&CUPnPLib::UPNP_SERVICE_LAYER3_FORWARDING
=
97 "urn:schemas-upnp-org:service:Layer3Forwarding:1";
98 const std::string
&CUPnPLib::UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG
=
99 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1";
100 const std::string
&CUPnPLib::UPNP_SERVICE_WAN_IP_CONNECTION
=
101 "urn:schemas-upnp-org:service:WANIPConnection:1";
102 const std::string
&CUPnPLib::UPNP_SERVICE_WAN_PPP_CONNECTION
=
103 "urn:schemas-upnp-org:service:WANPPPConnection:1";
106 CUPnPLib::CUPnPLib(CUPnPControlPoint
&ctrlPoint
)
108 m_ctrlPoint(ctrlPoint
)
113 std::string
CUPnPLib::GetUPnPErrorMessage(int code
) const
115 return UpnpGetErrorMessage(code
);
119 std::string
CUPnPLib::processUPnPErrorMessage(
120 const std::string
&messsage
,
122 const DOMString errorString
,
123 IXML_Document
*doc
) const
125 std::ostringstream msg
;
126 if (errorString
== NULL
|| *errorString
== 0) {
127 errorString
= "Not available";
134 CUPnPError
e(*this, doc
);
135 msg
<< e
.getErrorCode() <<
136 "', Error description :'" <<
137 e
.getErrorDescription() <<
141 "', Error description :'" <<
145 AddLogLineU(false, logUPnP
, msg
);
149 ": UPnP SDK error: " <<
150 GetUPnPErrorMessage(errorCode
) <<
151 " (" << errorCode
<< ").";
152 AddLogLineU(false, logUPnP
, msg
);
159 void CUPnPLib::ProcessActionResponse(
160 IXML_Document
*RespDoc
,
161 const std::string
&actionName
) const
163 std::ostringstream msg
;
165 IXML_Element
*root
= Element_GetRootElement(RespDoc
);
166 IXML_Element
*child
= Element_GetFirstChild(root
);
169 const DOMString childTag
= Element_GetTag(child
);
170 std::string childValue
= Element_GetTextValue(child
);
174 child
= Element_GetNextSibling(child
);
177 msg
<< "\n Empty response for action '" <<
180 AddDebugLogLineN(logUPnP
, msg
);
185 * \brief Returns the root node of a given document.
187 IXML_Element
*CUPnPLib::Element_GetRootElement(
188 IXML_Document
*doc
) const
190 IXML_Element
*root
= reinterpret_cast<IXML_Element
*>(
191 ixmlNode_getFirstChild(&doc
->n
));
198 * \brief Returns the first child of a given element.
200 IXML_Element
*CUPnPLib::Element_GetFirstChild(
201 IXML_Element
*parent
) const
203 IXML_Node
*child
= ixmlNode_getFirstChild(&parent
->n
);
205 return reinterpret_cast<IXML_Element
*>(child
);
210 * \brief Returns the next sibling of a given child.
212 IXML_Element
*CUPnPLib::Element_GetNextSibling(
213 IXML_Element
*child
) const
215 IXML_Node
*sibling
= ixmlNode_getNextSibling(&child
->n
);
217 return reinterpret_cast<IXML_Element
*>(sibling
);
222 * \brief Returns the element tag (name)
224 const DOMString
CUPnPLib::Element_GetTag(
225 IXML_Element
*element
) const
227 const DOMString tag
= ixmlNode_getNodeName(&element
->n
);
234 * \brief Returns the TEXT node value of the current node.
236 const std::string
CUPnPLib::Element_GetTextValue(
237 IXML_Element
*element
) const
240 return stdEmptyString
;
242 IXML_Node
*text
= ixmlNode_getFirstChild(&element
->n
);
243 const DOMString s
= ixmlNode_getNodeValue(text
);
254 * \brief Returns the TEXT node value of the first child matching tag.
256 const std::string
CUPnPLib::Element_GetChildValueByTag(
257 IXML_Element
*element
,
258 const DOMString tag
) const
260 IXML_Element
*child
=
261 Element_GetFirstChildByTag(element
, tag
);
263 return Element_GetTextValue(child
);
268 * \brief Returns the first child element that matches the requested tag or
271 IXML_Element
*CUPnPLib::Element_GetFirstChildByTag(
272 IXML_Element
*element
,
273 const DOMString tag
) const
275 if (!element
|| !tag
) {
279 IXML_Node
*child
= ixmlNode_getFirstChild(&element
->n
);
280 const DOMString childTag
= ixmlNode_getNodeName(child
);
281 while(child
&& childTag
&& strcmp(tag
, childTag
)) {
282 child
= ixmlNode_getNextSibling(child
);
283 childTag
= ixmlNode_getNodeName(child
);
286 return reinterpret_cast<IXML_Element
*>(child
);
291 * \brief Returns the next sibling element that matches the requested tag. Should be
292 * used with the return value of Element_GetFirstChildByTag().
294 IXML_Element
*CUPnPLib::Element_GetNextSiblingByTag(
295 IXML_Element
*element
, const DOMString tag
) const
297 if (!element
|| !tag
) {
301 IXML_Node
*child
= &element
->n
;
302 const DOMString childTag
= NULL
;
304 child
= ixmlNode_getNextSibling(child
);
305 childTag
= ixmlNode_getNodeName(child
);
306 } while(child
&& childTag
&& strcmp(tag
, childTag
));
308 return reinterpret_cast<IXML_Element
*>(child
);
312 const std::string
CUPnPLib::Element_GetAttributeByTag(
313 IXML_Element
*element
, const DOMString tag
) const
315 IXML_NamedNodeMap
*NamedNodeMap
= ixmlNode_getAttributes(&element
->n
);
316 IXML_Node
*attribute
= ixmlNamedNodeMap_getNamedItem(NamedNodeMap
, tag
);
317 const DOMString s
= ixmlNode_getNodeValue(attribute
);
322 ixmlNamedNodeMap_free(NamedNodeMap
);
328 CUPnPError::CUPnPError(
329 const CUPnPLib
&upnpLib
,
330 IXML_Document
*errorDoc
)
332 m_root (upnpLib
.Element_GetRootElement(errorDoc
)),
333 m_ErrorCode (upnpLib
.Element_GetChildValueByTag(m_root
, "errorCode")),
334 m_ErrorDescription(upnpLib
.Element_GetChildValueByTag(m_root
, "errorDescription"))
339 CUPnPArgument::CUPnPArgument(
340 const CUPnPControlPoint
&upnpControlPoint
,
342 IXML_Element
*argument
,
343 const std::string
&WXUNUSED(SCPDURL
))
345 m_UPnPControlPoint(upnpControlPoint
),
346 m_name (upnpLib
.Element_GetChildValueByTag(argument
, "name")),
347 m_direction (upnpLib
.Element_GetChildValueByTag(argument
, "direction")),
348 m_retval (upnpLib
.Element_GetFirstChildByTag(argument
, "retval")),
349 m_relatedStateVariable(upnpLib
.Element_GetChildValueByTag(argument
, "relatedStateVariable"))
351 std::ostringstream msg
;
352 msg
<< "\n Argument:" <<
353 "\n name: " << m_name
<<
354 "\n direction: " << m_direction
<<
355 "\n retval: " << m_retval
<<
356 "\n relatedStateVariable: " << m_relatedStateVariable
;
357 AddDebugLogLineN(logUPnP
, msg
);
361 CUPnPAction::CUPnPAction(
362 const CUPnPControlPoint
&upnpControlPoint
,
364 IXML_Element
*action
,
365 const std::string
&SCPDURL
)
367 m_UPnPControlPoint(upnpControlPoint
),
368 m_ArgumentList(upnpControlPoint
, upnpLib
, action
, SCPDURL
),
369 m_name(upnpLib
.Element_GetChildValueByTag(action
, "name"))
371 std::ostringstream msg
;
372 msg
<< "\n Action:" <<
373 "\n name: " << m_name
;
374 AddDebugLogLineN(logUPnP
, msg
);
378 CUPnPAllowedValue::CUPnPAllowedValue(
379 const CUPnPControlPoint
&upnpControlPoint
,
381 IXML_Element
*allowedValue
,
382 const std::string
&WXUNUSED(SCPDURL
))
384 m_UPnPControlPoint(upnpControlPoint
),
385 m_allowedValue(upnpLib
.Element_GetTextValue(allowedValue
))
387 std::ostringstream msg
;
388 msg
<< "\n AllowedValue:" <<
389 "\n allowedValue: " << m_allowedValue
;
390 AddDebugLogLineN(logUPnP
, msg
);
394 CUPnPStateVariable::CUPnPStateVariable(
395 const CUPnPControlPoint
&upnpControlPoint
,
397 IXML_Element
*stateVariable
,
398 const std::string
&SCPDURL
)
400 m_UPnPControlPoint(upnpControlPoint
),
401 m_AllowedValueList(upnpControlPoint
, upnpLib
, stateVariable
, SCPDURL
),
402 m_name (upnpLib
.Element_GetChildValueByTag(stateVariable
, "name")),
403 m_dataType (upnpLib
.Element_GetChildValueByTag(stateVariable
, "dataType")),
404 m_defaultValue(upnpLib
.Element_GetChildValueByTag(stateVariable
, "defaultValue")),
405 m_sendEvents (upnpLib
.Element_GetAttributeByTag (stateVariable
, "sendEvents"))
407 std::ostringstream msg
;
408 msg
<< "\n StateVariable:" <<
409 "\n name: " << m_name
<<
410 "\n dataType: " << m_dataType
<<
411 "\n defaultValue: " << m_defaultValue
<<
412 "\n sendEvents: " << m_sendEvents
;
413 AddDebugLogLineN(logUPnP
, msg
);
417 CUPnPSCPD::CUPnPSCPD(
418 const CUPnPControlPoint
&upnpControlPoint
,
421 const std::string
&SCPDURL
)
423 m_UPnPControlPoint(upnpControlPoint
),
424 m_ActionList(upnpControlPoint
, upnpLib
, scpd
, SCPDURL
),
425 m_ServiceStateTable(upnpControlPoint
, upnpLib
, scpd
, SCPDURL
),
431 CUPnPArgumentValue::CUPnPArgumentValue()
439 CUPnPArgumentValue::CUPnPArgumentValue(
440 const std::string
&argument
, const std::string
&value
)
442 m_argument(argument
),
448 CUPnPService::CUPnPService(
449 const CUPnPControlPoint
&upnpControlPoint
,
451 IXML_Element
*service
,
452 const std::string
&URLBase
)
454 m_UPnPControlPoint(upnpControlPoint
),
456 m_serviceType(upnpLib
.Element_GetChildValueByTag(service
, "serviceType")),
457 m_serviceId (upnpLib
.Element_GetChildValueByTag(service
, "serviceId")),
458 m_SCPDURL (upnpLib
.Element_GetChildValueByTag(service
, "SCPDURL")),
459 m_controlURL (upnpLib
.Element_GetChildValueByTag(service
, "controlURL")),
460 m_eventSubURL(upnpLib
.Element_GetChildValueByTag(service
, "eventSubURL")),
464 std::ostringstream msg
;
467 std::vector
<char> vscpdURL(URLBase
.length() + m_SCPDURL
.length() + 1);
468 char *scpdURL
= &vscpdURL
[0];
469 errcode
= UpnpResolveURL(
473 if( errcode
!= UPNP_E_SUCCESS
) {
474 msg
<< "Error generating scpdURL from " <<
475 "|" << URLBase
<< "|" <<
477 AddDebugLogLineN(logUPnP
, msg
);
479 m_absSCPDURL
= scpdURL
;
482 std::vector
<char> vcontrolURL(
483 URLBase
.length() + m_controlURL
.length() + 1);
484 char *controlURL
= &vcontrolURL
[0];
485 errcode
= UpnpResolveURL(
487 m_controlURL
.c_str(),
489 if( errcode
!= UPNP_E_SUCCESS
) {
490 msg
<< "Error generating controlURL from " <<
491 "|" << URLBase
<< "|" <<
492 m_controlURL
<< "|.";
493 AddDebugLogLineN(logUPnP
, msg
);
495 m_absControlURL
= controlURL
;
498 std::vector
<char> veventURL(
499 URLBase
.length() + m_eventSubURL
.length() + 1);
500 char *eventURL
= &veventURL
[0];
501 errcode
= UpnpResolveURL(
503 m_eventSubURL
.c_str(),
505 if( errcode
!= UPNP_E_SUCCESS
) {
506 msg
<< "Error generating eventURL from " <<
507 "|" << URLBase
<< "|" <<
508 m_eventSubURL
<< "|.";
509 AddDebugLogLineN(logUPnP
, msg
);
511 m_absEventSubURL
= eventURL
;
514 msg
<< "\n Service:" <<
515 "\n serviceType: " << m_serviceType
<<
516 "\n serviceId: " << m_serviceId
<<
517 "\n SCPDURL: " << m_SCPDURL
<<
518 "\n absSCPDURL: " << m_absSCPDURL
<<
519 "\n controlURL: " << m_controlURL
<<
520 "\n absControlURL: " << m_absControlURL
<<
521 "\n eventSubURL: " << m_eventSubURL
<<
522 "\n absEventSubURL: " << m_absEventSubURL
;
523 AddDebugLogLineN(logUPnP
, msg
);
525 if (m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_IP_CONNECTION
||
526 m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_PPP_CONNECTION
) {
528 m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_PPP_CONNECTION
||
529 m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG
||
530 m_serviceType
== upnpLib
.UPNP_SERVICE_LAYER3_FORWARDING
) {
533 //#warning Delete this code on release.
534 if (!upnpLib
.m_ctrlPoint
.WanServiceDetected()) {
535 // This condition can be used to suspend the parse
538 //#warning Delete this code when m_WanService is no longer used.
539 upnpLib
.m_ctrlPoint
.SetWanService(this);
542 msg
<< "WAN Service Detected: '" <<
543 m_serviceType
<< "'.";
544 AddDebugLogLineC(logUPnP
, msg
);
546 upnpLib
.m_ctrlPoint
.Subscribe(*this);
548 //#warning Delete this code on release.
551 msg
<< "WAN service detected again: '" <<
553 "'. Will only use the first instance.";
554 AddDebugLogLineC(logUPnP
, msg
);
559 msg
<< "Uninteresting service detected: '" <<
560 m_serviceType
<< "'. Ignoring.";
561 AddDebugLogLineC(logUPnP
, msg
);
566 CUPnPService::~CUPnPService()
571 bool CUPnPService::Execute(
572 const std::string
&ActionName
,
573 const std::vector
<CUPnPArgumentValue
> &ArgValue
) const
575 std::ostringstream msg
;
576 if (m_SCPD
.get() == NULL
) {
577 msg
<< "Service without SCPD Document, cannot execute action '" << ActionName
<<
578 "' for service '" << GetServiceType() << "'.";
579 AddDebugLogLineN(logUPnP
, msg
);
582 std::ostringstream
msgAction("Sending action ");
583 // Check for correct action name
584 ActionList::const_iterator itAction
=
585 m_SCPD
->GetActionList().find(ActionName
);
586 if (itAction
== m_SCPD
->GetActionList().end()) {
587 msg
<< "Invalid action name '" << ActionName
<<
588 "' for service '" << GetServiceType() << "'.";
589 AddDebugLogLineN(logUPnP
, msg
);
592 msgAction
<< ActionName
<< "(";
593 bool firstTime
= true;
594 // Check for correct Argument/Value pairs
595 const CUPnPAction
&action
= *(itAction
->second
);
596 for (unsigned int i
= 0; i
< ArgValue
.size(); ++i
) {
597 ArgumentList::const_iterator itArg
=
598 action
.GetArgumentList().find(ArgValue
[i
].GetArgument());
599 if (itArg
== action
.GetArgumentList().end()) {
600 msg
<< "Invalid argument name '" << ArgValue
[i
].GetArgument() <<
601 "' for action '" << action
.GetName() <<
602 "' for service '" << GetServiceType() << "'.";
603 AddDebugLogLineN(logUPnP
, msg
);
606 const CUPnPArgument
&argument
= *(itArg
->second
);
607 if (tolower(argument
.GetDirection()[0]) != 'i' ||
608 tolower(argument
.GetDirection()[1]) != 'n') {
609 msg
<< "Invalid direction for argument '" <<
610 ArgValue
[i
].GetArgument() <<
611 "' for action '" << action
.GetName() <<
612 "' for service '" << GetServiceType() << "'.";
613 AddDebugLogLineN(logUPnP
, msg
);
616 const std::string relatedStateVariableName
=
617 argument
.GetRelatedStateVariable();
618 if (!relatedStateVariableName
.empty()) {
619 ServiceStateTable::const_iterator itSVT
=
620 m_SCPD
->GetServiceStateTable().
621 find(relatedStateVariableName
);
622 if (itSVT
== m_SCPD
->GetServiceStateTable().end()) {
623 msg
<< "Inconsistent Service State Table, did not find '" <<
624 relatedStateVariableName
<<
625 "' for argument '" << argument
.GetName() <<
626 "' for action '" << action
.GetName() <<
627 "' for service '" << GetServiceType() << "'.";
628 AddDebugLogLineN(logUPnP
, msg
);
631 const CUPnPStateVariable
&stateVariable
= *(itSVT
->second
);
632 if ( !stateVariable
.GetAllowedValueList().empty() &&
633 stateVariable
.GetAllowedValueList().find(ArgValue
[i
].GetValue()) ==
634 stateVariable
.GetAllowedValueList().end()) {
635 msg
<< "Value not allowed '" << ArgValue
[i
].GetValue() <<
636 "' for state variable '" << relatedStateVariableName
<<
637 "' for argument '" << argument
.GetName() <<
638 "' for action '" << action
.GetName() <<
639 "' for service '" << GetServiceType() << "'.";
640 AddDebugLogLineN(logUPnP
, msg
);
650 ArgValue
[i
].GetArgument() <<
652 ArgValue
[i
].GetValue() <<
656 AddDebugLogLineN(logUPnP
, msgAction
);
657 // Everything is ok, make the action
658 IXML_Document
*ActionDoc
= NULL
;
659 if (!ArgValue
.empty()) {
660 for (unsigned int i
= 0; i
< ArgValue
.size(); ++i
) {
661 int ret
= UpnpAddToAction(
663 action
.GetName().c_str(),
664 GetServiceType().c_str(),
665 ArgValue
[i
].GetArgument().c_str(),
666 ArgValue
[i
].GetValue().c_str());
667 if (ret
!= UPNP_E_SUCCESS
) {
668 m_upnpLib
.processUPnPErrorMessage(
669 "UpnpAddToAction", ret
, NULL
, NULL
);
674 ActionDoc
= UpnpMakeAction(
675 action
.GetName().c_str(),
676 GetServiceType().c_str(),
679 msg
<< "Error: UpnpMakeAction returned NULL.";
680 AddLogLineU(false, logUPnP
, msg
);
685 // Send the action asynchronously
687 m_UPnPControlPoint
.GetUPnPClientHandle(),
688 GetAbsControlURL().c_str(),
689 GetServiceType().c_str(),
691 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
696 // Send the action synchronously
697 IXML_Document
*RespDoc
= NULL
;
698 int ret
= UpnpSendAction(
699 m_UPnPControlPoint
.GetUPnPClientHandle(),
700 GetAbsControlURL().c_str(),
701 GetServiceType().c_str(),
702 NULL
, ActionDoc
, &RespDoc
);
703 if (ret
!= UPNP_E_SUCCESS
) {
704 m_upnpLib
.processUPnPErrorMessage(
705 "UpnpSendAction", ret
, NULL
, RespDoc
);
706 ixmlDocument_free(ActionDoc
);
707 ixmlDocument_free(RespDoc
);
710 ixmlDocument_free(ActionDoc
);
712 // Check the response document
713 m_upnpLib
.ProcessActionResponse(
714 RespDoc
, action
.GetName());
716 // Free the response document
717 ixmlDocument_free(RespDoc
);
723 const std::string
CUPnPService::GetStateVariable(
724 const std::string
&stateVariableName
) const
726 std::ostringstream msg
;
728 int ret
= UpnpGetServiceVarStatus(
729 m_UPnPControlPoint
.GetUPnPClientHandle(),
730 GetAbsControlURL().c_str(),
731 stateVariableName
.c_str(),
733 if (ret
!= UPNP_E_SUCCESS
) {
734 msg
<< "GetStateVariable(\"" <<
736 "\"): in a call to UpnpGetServiceVarStatus";
737 m_upnpLib
.processUPnPErrorMessage(
738 msg
.str(), ret
, StVarVal
, NULL
);
739 return stdEmptyString
;
741 msg
<< "GetStateVariable: " <<
746 AddDebugLogLineN(logUPnP
, msg
);
747 return stdEmptyString
;
751 CUPnPDevice::CUPnPDevice(
752 const CUPnPControlPoint
&upnpControlPoint
,
754 IXML_Element
*device
,
755 const std::string
&URLBase
)
757 m_UPnPControlPoint(upnpControlPoint
),
758 m_DeviceList(upnpControlPoint
, upnpLib
, device
, URLBase
),
759 m_ServiceList(upnpControlPoint
, upnpLib
, device
, URLBase
),
760 m_deviceType (upnpLib
.Element_GetChildValueByTag(device
, "deviceType")),
761 m_friendlyName (upnpLib
.Element_GetChildValueByTag(device
, "friendlyName")),
762 m_manufacturer (upnpLib
.Element_GetChildValueByTag(device
, "manufacturer")),
763 m_manufacturerURL (upnpLib
.Element_GetChildValueByTag(device
, "manufacturerURL")),
764 m_modelDescription (upnpLib
.Element_GetChildValueByTag(device
, "modelDescription")),
765 m_modelName (upnpLib
.Element_GetChildValueByTag(device
, "modelName")),
766 m_modelNumber (upnpLib
.Element_GetChildValueByTag(device
, "modelNumber")),
767 m_modelURL (upnpLib
.Element_GetChildValueByTag(device
, "modelURL")),
768 m_serialNumber (upnpLib
.Element_GetChildValueByTag(device
, "serialNumber")),
769 m_UDN (upnpLib
.Element_GetChildValueByTag(device
, "UDN")),
770 m_UPC (upnpLib
.Element_GetChildValueByTag(device
, "UPC")),
771 m_presentationURL (upnpLib
.Element_GetChildValueByTag(device
, "presentationURL"))
773 std::ostringstream msg
;
774 int presURLlen
= strlen(URLBase
.c_str()) +
775 strlen(m_presentationURL
.c_str()) + 2;
776 std::vector
<char> vpresURL(presURLlen
);
777 char* presURL
= &vpresURL
[0];
778 int errcode
= UpnpResolveURL(
780 m_presentationURL
.c_str(),
782 if (errcode
!= UPNP_E_SUCCESS
) {
783 msg
<< "Error generating presentationURL from " <<
784 "|" << URLBase
<< "|" <<
785 m_presentationURL
<< "|.";
786 AddDebugLogLineN(logUPnP
, msg
);
788 m_presentationURL
= presURL
;
792 msg
<< "\n Device: " <<
793 "\n friendlyName: " << m_friendlyName
<<
794 "\n deviceType: " << m_deviceType
<<
795 "\n manufacturer: " << m_manufacturer
<<
796 "\n manufacturerURL: " << m_manufacturerURL
<<
797 "\n modelDescription: " << m_modelDescription
<<
798 "\n modelName: " << m_modelName
<<
799 "\n modelNumber: " << m_modelNumber
<<
800 "\n modelURL: " << m_modelURL
<<
801 "\n serialNumber: " << m_serialNumber
<<
802 "\n UDN: " << m_UDN
<<
803 "\n UPC: " << m_UPC
<<
804 "\n presentationURL: " << m_presentationURL
;
805 AddDebugLogLineN(logUPnP
, msg
);
809 CUPnPRootDevice::CUPnPRootDevice(
810 const CUPnPControlPoint
&upnpControlPoint
,
812 IXML_Element
*rootDevice
,
813 const std::string
&OriginalURLBase
,
814 const std::string
&FixedURLBase
,
815 const char *location
,
818 CUPnPDevice(upnpControlPoint
, upnpLib
, rootDevice
, FixedURLBase
),
819 m_UPnPControlPoint(upnpControlPoint
),
820 m_URLBase(OriginalURLBase
),
821 m_location(location
),
824 std::ostringstream msg
;
826 "\n Root Device: " <<
827 "\n URLBase: " << m_URLBase
<<
828 "\n Fixed URLBase: " << FixedURLBase
<<
829 "\n location: " << m_location
<<
830 "\n expires: " << m_expires
;
831 AddDebugLogLineN(logUPnP
, msg
);
835 CUPnPControlPoint
*CUPnPControlPoint::s_CtrlPoint
= NULL
;
838 CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort
)
841 m_UPnPClientHandle(),
844 m_ActivePortMappingsMap(),
845 m_RootDeviceListMutex(),
846 m_IGWDeviceDetected(false),
851 // Null string at first
852 std::ostringstream msg
;
856 char *ipAddress
= NULL
;
857 unsigned short port
= 0;
858 ret
= UpnpInit(ipAddress
, udpPort
);
859 if (ret
!= UPNP_E_SUCCESS
) {
860 msg
<< "error(UpnpInit): Error code ";
863 port
= UpnpGetServerPort();
864 ipAddress
= UpnpGetServerIpAddress();
865 msg
<< "bound to " << ipAddress
<< ":" <<
867 AddLogLineU(false, logUPnP
, msg
);
869 ret
= UpnpRegisterClient(
870 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
872 &m_UPnPClientHandle
);
873 if (ret
!= UPNP_E_SUCCESS
) {
874 msg
<< "error(UpnpRegisterClient): Error registering callback: ";
878 // We could ask for just the right device here. If the root device
879 // contains the device we want, it will respond with the full XML doc,
880 // including the root device and every sub-device it has.
882 // But lets find out what we have in our network by calling UPNP_ROOT_DEVICE.
884 // We should not search twice, because this will produce two
885 // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
887 ret
= UpnpSearchAsync(m_UPnPClientHandle
, 3, m_upnpLib
.UPNP_ROOT_DEVICE
.c_str(), NULL
);
888 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this);
889 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this);
890 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this);
891 if (ret
!= UPNP_E_SUCCESS
) {
892 msg
<< "error(UpnpSearchAsync): Error sending search request: ";
896 // Wait for the UPnP initialization to complete.
898 // Lock the search timeout mutex
899 m_WaitForSearchTimeoutMutex
.Lock();
901 // Lock it again, so that we block. Unlocking will only happen
902 // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
904 CUPnPMutexLocker
lock(m_WaitForSearchTimeoutMutex
);
911 msg
<< ret
<< ": " << m_upnpLib
.GetUPnPErrorMessage(ret
) << ".";
912 throw CUPnPException(msg
);
916 CUPnPControlPoint::~CUPnPControlPoint()
918 for( RootDeviceMap::iterator it
= m_RootDeviceMap
.begin();
919 it
!= m_RootDeviceMap
.end();
925 UpnpUnRegisterClient(m_UPnPClientHandle
);
930 bool CUPnPControlPoint::AddPortMappings(
931 std::vector
<CUPnPPortMapping
> &upnpPortMapping
)
933 std::ostringstream msg
;
934 if (!WanServiceDetected()) {
935 msg
<< "UPnP Error: "
936 "CUPnPControlPoint::AddPortMapping: "
937 "WAN Service not detected.";
938 AddLogLineU(true, logUPnP
, msg
);
942 int n
= upnpPortMapping
.size();
945 // Check the number of port mappings before
946 std::istringstream
PortMappingNumberOfEntries(
947 m_WanService
->GetStateVariable(
948 "PortMappingNumberOfEntries"));
949 unsigned long oldNumberOfEntries
;
950 PortMappingNumberOfEntries
>> oldNumberOfEntries
;
952 // Add the enabled port mappings
953 for (int i
= 0; i
< n
; ++i
) {
954 if (upnpPortMapping
[i
].getEnabled() == "1") {
955 // Add the mapping to the control point
956 // active mappings list
957 m_ActivePortMappingsMap
[upnpPortMapping
[i
].getKey()] =
960 // Add the port mapping
961 PrivateAddPortMapping(upnpPortMapping
[i
]);
965 // Test some variables, this is deprecated, might not work
967 m_WanService
->GetStateVariable("ConnectionType");
968 m_WanService
->GetStateVariable("PossibleConnectionTypes");
969 m_WanService
->GetStateVariable("ConnectionStatus");
970 m_WanService
->GetStateVariable("Uptime");
971 m_WanService
->GetStateVariable("LastConnectionError");
972 m_WanService
->GetStateVariable("RSIPAvailable");
973 m_WanService
->GetStateVariable("NATEnabled");
974 m_WanService
->GetStateVariable("ExternalIPAddress");
975 m_WanService
->GetStateVariable("PortMappingNumberOfEntries");
976 m_WanService
->GetStateVariable("PortMappingLeaseDuration");
979 std::vector
<CUPnPArgumentValue
> argval
;
981 m_WanService
->Execute("GetStatusInfo", argval
);
984 // These do not work. Their value must be requested for a
985 // specific port mapping.
986 m_WanService
->GetStateVariable("PortMappingEnabled");
987 m_WanService
->GetStateVariable("RemoteHost");
988 m_WanService
->GetStateVariable("ExternalPort");
989 m_WanService
->GetStateVariable("InternalPort");
990 m_WanService
->GetStateVariable("PortMappingProtocol");
991 m_WanService
->GetStateVariable("InternalClient");
992 m_WanService
->GetStateVariable("PortMappingDescription");
997 msg
<< "CUPnPControlPoint::DeletePortMappings: "
998 "m_ActivePortMappingsMap.size() == " <<
999 m_ActivePortMappingsMap
.size();
1000 AddDebugLogLineN(logUPnP
, msg
);
1002 // Not very good, must find a better test
1003 PortMappingNumberOfEntries
.str(
1004 m_WanService
->GetStateVariable(
1005 "PortMappingNumberOfEntries"));
1006 unsigned long newNumberOfEntries
;
1007 PortMappingNumberOfEntries
>> newNumberOfEntries
;
1008 ok
= newNumberOfEntries
- oldNumberOfEntries
== 4;
1014 void CUPnPControlPoint::RefreshPortMappings()
1016 for ( PortMappingMap::iterator it
= m_ActivePortMappingsMap
.begin();
1017 it
!= m_ActivePortMappingsMap
.end();
1019 PrivateAddPortMapping(it
->second
);
1023 m_WanService
->GetStateVariable("PortMappingNumberOfEntries");
1027 bool CUPnPControlPoint::PrivateAddPortMapping(
1028 CUPnPPortMapping
&upnpPortMapping
)
1030 // Get an IP address. The UPnP server one must do.
1031 std::string
ipAddress(UpnpGetServerIpAddress());
1033 // Start building the action
1034 std::string
actionName("AddPortMapping");
1035 std::vector
<CUPnPArgumentValue
> argval(8);
1037 // Action parameters
1038 argval
[0].SetArgument("NewRemoteHost");
1039 argval
[0].SetValue("");
1040 argval
[1].SetArgument("NewExternalPort");
1041 argval
[1].SetValue(upnpPortMapping
.getPort());
1042 argval
[2].SetArgument("NewProtocol");
1043 argval
[2].SetValue(upnpPortMapping
.getProtocol());
1044 argval
[3].SetArgument("NewInternalPort");
1045 argval
[3].SetValue(upnpPortMapping
.getPort());
1046 argval
[4].SetArgument("NewInternalClient");
1047 argval
[4].SetValue(ipAddress
);
1048 argval
[5].SetArgument("NewEnabled");
1049 argval
[5].SetValue("1");
1050 argval
[6].SetArgument("NewPortMappingDescription");
1051 argval
[6].SetValue(upnpPortMapping
.getDescription());
1052 argval
[7].SetArgument("NewLeaseDuration");
1053 argval
[7].SetValue("0");
1057 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1058 it
!= m_ServiceMap
.end(); ++it
) {
1059 ret
&= it
->second
->Execute(actionName
, argval
);
1066 bool CUPnPControlPoint::DeletePortMappings(
1067 std::vector
<CUPnPPortMapping
> &upnpPortMapping
)
1069 std::ostringstream msg
;
1070 if (!WanServiceDetected()) {
1071 msg
<< "UPnP Error: "
1072 "CUPnPControlPoint::DeletePortMapping: "
1073 "WAN Service not detected.";
1074 AddLogLineU(true, logUPnP
, msg
);
1078 int n
= upnpPortMapping
.size();
1081 // Check the number of port mappings before
1082 std::istringstream
PortMappingNumberOfEntries(
1083 m_WanService
->GetStateVariable(
1084 "PortMappingNumberOfEntries"));
1085 unsigned long oldNumberOfEntries
;
1086 PortMappingNumberOfEntries
>> oldNumberOfEntries
;
1088 // Delete the enabled port mappings
1089 for (int i
= 0; i
< n
; ++i
) {
1090 if (upnpPortMapping
[i
].getEnabled() == "1") {
1091 // Delete the mapping from the control point
1092 // active mappings list
1093 PortMappingMap::iterator it
=
1094 m_ActivePortMappingsMap
.find(
1095 upnpPortMapping
[i
].getKey());
1096 if (it
!= m_ActivePortMappingsMap
.end()) {
1097 m_ActivePortMappingsMap
.erase(it
);
1099 msg
<< "UPnP Error: "
1100 "CUPnPControlPoint::DeletePortMapping: "
1101 "Mapping was not found in the active "
1103 AddLogLineU(true, logUPnP
, msg
);
1106 // Delete the port mapping
1107 PrivateDeletePortMapping(upnpPortMapping
[i
]);
1113 msg
<< "CUPnPControlPoint::DeletePortMappings: "
1114 "m_ActivePortMappingsMap.size() == " <<
1115 m_ActivePortMappingsMap
.size();
1116 AddDebugLogLineN(logUPnP
, msg
);
1118 // Not very good, must find a better test
1119 PortMappingNumberOfEntries
.str(
1120 m_WanService
->GetStateVariable(
1121 "PortMappingNumberOfEntries"));
1122 unsigned long newNumberOfEntries
;
1123 PortMappingNumberOfEntries
>> newNumberOfEntries
;
1124 ok
= oldNumberOfEntries
- newNumberOfEntries
== 4;
1130 bool CUPnPControlPoint::PrivateDeletePortMapping(
1131 CUPnPPortMapping
&upnpPortMapping
)
1133 // Start building the action
1134 std::string
actionName("DeletePortMapping");
1135 std::vector
<CUPnPArgumentValue
> argval(3);
1137 // Action parameters
1138 argval
[0].SetArgument("NewRemoteHost");
1139 argval
[0].SetValue("");
1140 argval
[1].SetArgument("NewExternalPort");
1141 argval
[1].SetValue(upnpPortMapping
.getPort());
1142 argval
[2].SetArgument("NewProtocol");
1143 argval
[2].SetValue(upnpPortMapping
.getProtocol());
1147 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1148 it
!= m_ServiceMap
.end(); ++it
) {
1149 ret
&= it
->second
->Execute(actionName
, argval
);
1156 // This function is static
1157 int CUPnPControlPoint::Callback(Upnp_EventType EventType
, void *Event
, void * /*Cookie*/)
1159 std::ostringstream msg
;
1160 std::ostringstream msg2
;
1161 // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
1162 // happen with a wrong cookie and... boom!
1163 // CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
1164 CUPnPControlPoint
*upnpCP
= CUPnPControlPoint::s_CtrlPoint
;
1166 //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
1167 switch (EventType
) {
1168 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
:
1169 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
1170 msg
<< "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): ";
1171 msg2
<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
1173 case UPNP_DISCOVERY_SEARCH_RESULT
: {
1174 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n");
1175 msg
<< "error(UPNP_DISCOVERY_SEARCH_RESULT): ";
1176 msg2
<< "UPNP_DISCOVERY_SEARCH_RESULT: ";
1179 struct Upnp_Discovery
*d_event
= (struct Upnp_Discovery
*)Event
;
1180 IXML_Document
*doc
= NULL
;
1182 if (d_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1183 msg
<< upnpCP
->m_upnpLib
.GetUPnPErrorMessage(d_event
->ErrCode
) << ".";
1184 AddDebugLogLineC(logUPnP
, msg
);
1186 // Get the XML tree device description in doc
1187 ret
= UpnpDownloadXmlDoc(d_event
->Location
, &doc
);
1188 if (ret
!= UPNP_E_SUCCESS
) {
1189 msg
<< "Error retrieving device description from " <<
1190 d_event
->Location
<< ": " <<
1191 upnpCP
->m_upnpLib
.GetUPnPErrorMessage(ret
) <<
1193 AddDebugLogLineC(logUPnP
, msg
);
1195 msg2
<< "Retrieving device description from " <<
1196 d_event
->Location
<< ".";
1197 AddDebugLogLineN(logUPnP
, msg2
);
1200 // Get the root node
1201 IXML_Element
*root
=
1202 upnpCP
->m_upnpLib
.Element_GetRootElement(doc
);
1203 // Extract the URLBase
1204 const std::string urlBase
= upnpCP
->m_upnpLib
.
1205 Element_GetChildValueByTag(root
, "URLBase");
1206 // Get the root device
1207 IXML_Element
*rootDevice
= upnpCP
->m_upnpLib
.
1208 Element_GetFirstChildByTag(root
, "device");
1209 // Extract the deviceType
1210 std::string
devType(upnpCP
->m_upnpLib
.
1211 Element_GetChildValueByTag(rootDevice
, "deviceType"));
1212 // Only add device if it is an InternetGatewayDevice
1213 if (stdStringIsEqualCI(devType
, upnpCP
->m_upnpLib
.UPNP_DEVICE_IGW
)) {
1214 // This condition can be used to auto-detect
1215 // the UPnP device we are interested in.
1216 // Obs.: Don't block the entry here on this
1217 // condition! There may be more than one device,
1218 // and the first that enters may not be the one
1219 // we are interested in!
1220 upnpCP
->SetIGWDeviceDetected(true);
1221 // Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
1222 // we don't want to spam our logs.
1223 if (EventType
!= UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
) {
1224 msg
.str("Internet Gateway Device Detected.");
1225 AddLogLineU(true, logUPnP
, msg
);
1227 // Add the root device to our list
1228 upnpCP
->AddRootDevice(rootDevice
, urlBase
,
1229 d_event
->Location
, d_event
->Expires
);
1231 // Free the XML doc tree
1232 ixmlDocument_free(doc
);
1236 case UPNP_DISCOVERY_SEARCH_TIMEOUT
: {
1237 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1239 msg
<< "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1240 AddDebugLogLineN(logUPnP
, msg
);
1242 // Unlock the search timeout mutex
1243 upnpCP
->m_WaitForSearchTimeoutMutex
.Unlock();
1247 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE
: {
1248 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
1249 // UPnP Device Removed
1250 struct Upnp_Discovery
*dab_event
= (struct Upnp_Discovery
*)Event
;
1251 if (dab_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1252 msg
<< "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
1253 upnpCP
->m_upnpLib
.GetUPnPErrorMessage(dab_event
->ErrCode
) <<
1255 AddDebugLogLineC(logUPnP
, msg
);
1257 std::string devType
= dab_event
->DeviceType
;
1258 // Check for an InternetGatewayDevice and removes it from the list
1259 std::transform(devType
.begin(), devType
.end(), devType
.begin(), tolower
);
1260 if (stdStringIsEqualCI(devType
, upnpCP
->m_upnpLib
.UPNP_DEVICE_IGW
)) {
1261 upnpCP
->RemoveRootDevice(dab_event
->DeviceId
);
1265 case UPNP_EVENT_RECEIVED
: {
1266 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1268 struct Upnp_Event
*e_event
= (struct Upnp_Event
*)Event
;
1269 const std::string Sid
= e_event
->Sid
;
1271 upnpCP
->OnEventReceived(Sid
, e_event
->EventKey
, e_event
->ChangedVariables
);
1274 case UPNP_EVENT_SUBSCRIBE_COMPLETE
:
1275 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
1276 msg
<< "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): ";
1277 goto upnpEventRenewalComplete
;
1278 case UPNP_EVENT_UNSUBSCRIBE_COMPLETE
:
1279 //fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
1280 msg
<< "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): ";
1281 goto upnpEventRenewalComplete
;
1282 case UPNP_EVENT_RENEWAL_COMPLETE
: {
1283 //fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n");
1284 msg
<< "error(UPNP_EVENT_RENEWAL_COMPLETE): ";
1285 upnpEventRenewalComplete
:
1286 struct Upnp_Event_Subscribe
*es_event
=
1287 (struct Upnp_Event_Subscribe
*)Event
;
1288 if (es_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1289 msg
<< "Error in Event Subscribe Callback";
1290 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1291 msg
.str(), es_event
->ErrCode
, NULL
, NULL
);
1294 TvCtrlPointHandleSubscribeUpdate(
1295 es_event
->PublisherUrl
,
1297 es_event
->TimeOut
);
1304 case UPNP_EVENT_AUTORENEWAL_FAILED
:
1305 //fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n");
1306 msg
<< "error(UPNP_EVENT_AUTORENEWAL_FAILED): ";
1307 msg2
<< "UPNP_EVENT_AUTORENEWAL_FAILED: ";
1308 goto upnpEventSubscriptionExpired
;
1309 case UPNP_EVENT_SUBSCRIPTION_EXPIRED
: {
1310 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
1311 msg
<< "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): ";
1312 msg2
<< "UPNP_EVENT_SUBSCRIPTION_EXPIRED: ";
1313 upnpEventSubscriptionExpired
:
1314 struct Upnp_Event_Subscribe
*es_event
=
1315 (struct Upnp_Event_Subscribe
*)Event
;
1318 int ret
= UpnpSubscribe(
1319 upnpCP
->m_UPnPClientHandle
,
1320 es_event
->PublisherUrl
,
1323 if (ret
!= UPNP_E_SUCCESS
) {
1324 msg
<< "Error Subscribing to EventURL";
1325 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1326 msg
.str(), es_event
->ErrCode
, NULL
, NULL
);
1328 ServiceMap::iterator it
=
1329 upnpCP
->m_ServiceMap
.find(es_event
->PublisherUrl
);
1330 if (it
!= upnpCP
->m_ServiceMap
.end()) {
1331 CUPnPService
&service
= *(it
->second
);
1332 service
.SetTimeout(TimeOut
);
1333 service
.SetSID(newSID
);
1334 msg2
<< "Re-subscribed to EventURL '" <<
1335 es_event
->PublisherUrl
<<
1336 "' with SID == '" <<
1338 AddDebugLogLineC(logUPnP
, msg2
);
1339 // In principle, we should test to see if the
1340 // service is the same. But here we only have one
1342 upnpCP
->RefreshPortMappings();
1344 msg
<< "Error: did not find service " <<
1345 newSID
<< " in the service map.";
1346 AddDebugLogLineC(logUPnP
, msg
);
1351 case UPNP_CONTROL_ACTION_COMPLETE
: {
1352 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n");
1353 // This is here if we choose to do this asynchronously
1354 struct Upnp_Action_Complete
*a_event
=
1355 (struct Upnp_Action_Complete
*)Event
;
1356 if (a_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1357 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1358 "UpnpSendActionAsync",
1359 a_event
->ErrCode
, NULL
,
1360 a_event
->ActionResult
);
1362 // Check the response document
1363 upnpCP
->m_upnpLib
.ProcessActionResponse(
1364 a_event
->ActionResult
,
1365 "<UpnpSendActionAsync>");
1367 /* No need for any processing here, just print out results.
1368 * Service state table updates are handled by events.
1372 case UPNP_CONTROL_GET_VAR_COMPLETE
: {
1373 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n");
1374 msg
<< "error(UPNP_CONTROL_GET_VAR_COMPLETE): ";
1375 struct Upnp_State_Var_Complete
*sv_event
=
1376 (struct Upnp_State_Var_Complete
*)Event
;
1377 if (sv_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1378 msg
<< "m_UpnpGetServiceVarStatusAsync";
1379 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1380 msg
.str(), sv_event
->ErrCode
, NULL
, NULL
);
1383 // Warning: The use of UpnpGetServiceVarStatus and
1384 // UpnpGetServiceVarStatusAsync is deprecated by the
1386 TvCtrlPointHandleGetVar(
1388 sv_event
->StateVarName
,
1389 sv_event
->CurrentVal
);
1394 // ignore these cases, since this is not a device
1395 case UPNP_CONTROL_GET_VAR_REQUEST
:
1396 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n");
1397 msg
<< "error(UPNP_CONTROL_GET_VAR_REQUEST): ";
1398 goto eventSubscriptionRequest
;
1399 case UPNP_CONTROL_ACTION_REQUEST
:
1400 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n");
1401 msg
<< "error(UPNP_CONTROL_ACTION_REQUEST): ";
1402 goto eventSubscriptionRequest
;
1403 case UPNP_EVENT_SUBSCRIPTION_REQUEST
:
1404 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
1405 msg
<< "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): ";
1406 eventSubscriptionRequest
:
1407 msg
<< "This is not a UPnP Device, this is a UPnP Control Point, event ignored.";
1408 AddDebugLogLineC(logUPnP
, msg
);
1411 // Humm, this is not good, we forgot to handle something...
1413 "Callback: default... Unknown event:'%d', not good.\n",
1415 msg
<< "error(UPnP::Callback): Event not handled:'" <<
1417 fprintf(stderr
, "%s\n", msg
.str().c_str());
1418 AddDebugLogLineC(logUPnP
, msg
);
1419 // Better not throw in the callback. Who would catch it?
1420 //throw CUPnPException(msg);
1428 void CUPnPControlPoint::OnEventReceived(
1429 const std::string
&Sid
,
1431 IXML_Document
*ChangedVariablesDoc
)
1433 std::ostringstream msg
;
1434 msg
<< "UPNP_EVENT_RECEIVED:" <<
1435 "\n SID: " << Sid
<<
1436 "\n Key: " << EventKey
<<
1437 "\n Property list:";
1438 IXML_Element
*root
=
1439 m_upnpLib
.Element_GetRootElement(ChangedVariablesDoc
);
1440 IXML_Element
*child
=
1441 m_upnpLib
.Element_GetFirstChild(root
);
1444 IXML_Element
*child2
=
1445 m_upnpLib
.Element_GetFirstChild(child
);
1446 const DOMString childTag
=
1447 m_upnpLib
.Element_GetTag(child2
);
1448 std::string childValue
=
1449 m_upnpLib
.Element_GetTextValue(child2
);
1453 child
= m_upnpLib
.Element_GetNextSibling(child
);
1456 msg
<< "\n Empty property list.";
1458 AddDebugLogLineC(logUPnP
, msg
);
1459 // Freeing that doc segfaults. Probably should not be freed.
1460 //ixmlDocument_free(ChangedVariablesDoc);
1464 void CUPnPControlPoint::AddRootDevice(
1465 IXML_Element
*rootDevice
, const std::string
&urlBase
,
1466 const char *location
, int expires
)
1468 // Lock the Root Device List
1469 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1471 // Root node's URLBase
1472 std::string
OriginalURLBase(urlBase
);
1473 std::string
FixedURLBase(OriginalURLBase
.empty() ?
1477 // Get the UDN (Unique Device Name)
1479 m_upnpLib
.Element_GetChildValueByTag(rootDevice
, "UDN"));
1480 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1481 bool alreadyAdded
= it
!= m_RootDeviceMap
.end();
1483 // Just set the expires field
1484 it
->second
->SetExpires(expires
);
1486 // Add a new root device to the root device list
1487 CUPnPRootDevice
*upnpRootDevice
= new CUPnPRootDevice(
1488 *this, m_upnpLib
, rootDevice
,
1489 OriginalURLBase
, FixedURLBase
,
1491 m_RootDeviceMap
[upnpRootDevice
->GetUDN()] = upnpRootDevice
;
1496 void CUPnPControlPoint::RemoveRootDevice(const char *udn
)
1498 // Lock the Root Device List
1499 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1502 std::string
UDN(udn
);
1503 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1504 if (it
!= m_RootDeviceMap
.end()) {
1506 m_RootDeviceMap
.erase(UDN
);
1511 void CUPnPControlPoint::Subscribe(CUPnPService
&service
)
1513 std::ostringstream msg
;
1515 IXML_Document
*scpdDoc
= NULL
;
1516 int errcode
= UpnpDownloadXmlDoc(
1517 service
.GetAbsSCPDURL().c_str(), &scpdDoc
);
1518 if (errcode
== UPNP_E_SUCCESS
) {
1519 // Get the root node of this service (the SCPD Document)
1520 IXML_Element
*scpdRoot
=
1521 m_upnpLib
.Element_GetRootElement(scpdDoc
);
1522 CUPnPSCPD
*scpd
= new CUPnPSCPD(*this, m_upnpLib
,
1523 scpdRoot
, service
.GetAbsSCPDURL());
1524 service
.SetSCPD(scpd
);
1525 m_ServiceMap
[service
.GetAbsEventSubURL()] = &service
;
1526 msg
<< "Successfully retrieved SCPD Document for service " <<
1527 service
.GetServiceType() << ", absEventSubURL: " <<
1528 service
.GetAbsEventSubURL() << ".";
1529 AddLogLineU(true, logUPnP
, msg
);
1532 // Now try to subscribe to this service. If the subscription
1533 // is not successfull, we will not be notified about events,
1534 // but it may be possible to use the service anyway.
1535 errcode
= UpnpSubscribe(m_UPnPClientHandle
,
1536 service
.GetAbsEventSubURL().c_str(),
1537 service
.GetTimeoutAddr(),
1539 if (errcode
== UPNP_E_SUCCESS
) {
1540 msg
<< "Successfully subscribed to service " <<
1541 service
.GetServiceType() << ", absEventSubURL: " <<
1542 service
.GetAbsEventSubURL() << ".";
1543 AddLogLineU(true, logUPnP
, msg
);
1545 msg
<< "Error subscribing to service " <<
1546 service
.GetServiceType() << ", absEventSubURL: " <<
1547 service
.GetAbsEventSubURL() << ", error: " <<
1548 m_upnpLib
.GetUPnPErrorMessage(errcode
) << ".";
1552 msg
<< "Error getting SCPD Document from " <<
1553 service
.GetAbsSCPDURL() << ".";
1554 AddLogLineU(true, logUPnP
, msg
);
1561 AddLogLineU(true, logUPnP
, msg
);
1565 void CUPnPControlPoint::Unsubscribe(CUPnPService
&service
)
1567 ServiceMap::iterator it
= m_ServiceMap
.find(service
.GetAbsEventSubURL());
1568 if (it
!= m_ServiceMap
.end()) {
1569 m_ServiceMap
.erase(it
);
1570 UpnpUnSubscribe(m_UPnPClientHandle
, service
.GetSID());
1574 #endif /* ENABLE_UPNP */