2 // This file is part of the aMule Project.
4 // Copyright (c) 2004-2008 Marcelo Roberto Jimenez ( phoenix@amule.org )
5 // Copyright (c) 2006-2008 aMule Team ( admin@amule.org / http://www.amule.org )
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
31 #include <dlfcn.h> // For dlopen(), dlsym(), dlclose()
32 #include <algorithm> // For transform()
37 #define REINTERPRET_CAST(x) reinterpret_cast<x>
40 #ifndef REINTERPRET_CAST
41 // Let's hope that function pointers are equal in size to data pointers
42 #define REINTERPRET_CAST(x) (x)
47 * Case insensitive std::string comparison
49 bool stdStringIsEqualCI(const std::string
&s1
, const std::string
&s2
)
53 std::transform(ns1
.begin(), ns1
.end(), ns1
.begin(), tolower
);
54 std::transform(ns2
.begin(), ns2
.end(), ns2
.begin(), tolower
);
59 CUPnPPortMapping::CUPnPPortMapping(
61 const std::string
&protocol
,
63 const std::string
&description
)
67 m_enabled(enabled
? "1" : "0"),
68 m_description(description
),
71 std::ostringstream oss
;
74 m_key
= m_protocol
+ m_port
;
78 const std::string
&CUPnPLib::UPNP_ROOT_DEVICE
=
81 const std::string
&CUPnPLib::UPNP_DEVICE_IGW
=
82 "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
83 const std::string
&CUPnPLib::UPNP_DEVICE_WAN
=
84 "urn:schemas-upnp-org:device:WANDevice:1";
85 const std::string
&CUPnPLib::UPNP_DEVICE_WAN_CONNECTION
=
86 "urn:schemas-upnp-org:device:WANConnectionDevice:1";
87 const std::string
&CUPnPLib::UPNP_DEVICE_LAN
=
88 "urn:schemas-upnp-org:device:LANDevice:1";
90 const std::string
&CUPnPLib::UPNP_SERVICE_LAYER3_FORWARDING
=
91 "urn:schemas-upnp-org:service:Layer3Forwarding:1";
92 const std::string
&CUPnPLib::UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG
=
93 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1";
94 const std::string
&CUPnPLib::UPNP_SERVICE_WAN_IP_CONNECTION
=
95 "urn:schemas-upnp-org:service:WANIPConnection:1";
96 const std::string
&CUPnPLib::UPNP_SERVICE_WAN_PPP_CONNECTION
=
97 "urn:schemas-upnp-org:service:WANPPPConnection:1";
100 CUPnPLib::CUPnPLib(CUPnPControlPoint
&ctrlPoint
)
102 m_ctrlPoint(ctrlPoint
)
107 std::string
CUPnPLib::GetUPnPErrorMessage(int code
) const
109 return UpnpGetErrorMessage(code
);
113 std::string
CUPnPLib::processUPnPErrorMessage(
114 const std::string
&messsage
,
116 const DOMString errorString
,
117 IXML_Document
*doc
) const
119 std::ostringstream msg
;
120 if (errorString
== NULL
|| *errorString
== 0) {
121 errorString
= "Not available";
128 CUPnPError
e(*this, doc
);
129 msg
<< e
.getErrorCode() <<
130 "', Error description :'" <<
131 e
.getErrorDescription() <<
135 "', Error description :'" <<
139 AddLogLineU(false, logUPnP
, msg
);
143 ": UPnP SDK error: " <<
144 GetUPnPErrorMessage(errorCode
) <<
145 " (" << errorCode
<< ").";
146 AddLogLineU(false, logUPnP
, msg
);
153 void CUPnPLib::ProcessActionResponse(
154 IXML_Document
*RespDoc
,
155 const std::string
&actionName
) const
157 std::ostringstream msg
;
159 IXML_Element
*root
= Element_GetRootElement(RespDoc
);
160 IXML_Element
*child
= Element_GetFirstChild(root
);
163 const DOMString childTag
= Element_GetTag(child
);
164 std::string childValue
= Element_GetTextValue(child
);
168 child
= Element_GetNextSibling(child
);
171 msg
<< "\n Empty response for action '" <<
174 AddDebugLogLineM(false, logUPnP
, msg
);
179 * \brief Returns the root node of a given document.
181 IXML_Element
*CUPnPLib::Element_GetRootElement(
182 IXML_Document
*doc
) const
184 IXML_Element
*root
= REINTERPRET_CAST(IXML_Element
*)(
185 ixmlNode_getFirstChild(
186 REINTERPRET_CAST(IXML_Node
*)(doc
)));
193 * \brief Returns the first child of a given element.
195 IXML_Element
*CUPnPLib::Element_GetFirstChild(
196 IXML_Element
*parent
) const
198 IXML_Node
*node
= REINTERPRET_CAST(IXML_Node
*)(parent
);
199 IXML_Node
*child
= ixmlNode_getFirstChild(node
);
201 return REINTERPRET_CAST(IXML_Element
*)(child
);
206 * \brief Returns the next sibling of a given child.
208 IXML_Element
*CUPnPLib::Element_GetNextSibling(
209 IXML_Element
*child
) const
211 IXML_Node
*node
= REINTERPRET_CAST(IXML_Node
*)(child
);
212 IXML_Node
*sibling
= ixmlNode_getNextSibling(node
);
214 return REINTERPRET_CAST(IXML_Element
*)(sibling
);
219 * \brief Returns the element tag (name)
221 const DOMString
CUPnPLib::Element_GetTag(
222 IXML_Element
*element
) const
224 IXML_Node
*node
= REINTERPRET_CAST(IXML_Node
*)(element
);
225 const DOMString tag
= ixmlNode_getNodeName(node
);
232 * \brief Returns the TEXT node value of the current node.
234 const std::string
CUPnPLib::Element_GetTextValue(
235 IXML_Element
*element
) const
238 return stdEmptyString
;
240 IXML_Node
*text
= ixmlNode_getFirstChild(
241 REINTERPRET_CAST(IXML_Node
*)(element
));
242 const DOMString s
= ixmlNode_getNodeValue(text
);
253 * \brief Returns the TEXT node value of the first child matching tag.
255 const std::string
CUPnPLib::Element_GetChildValueByTag(
256 IXML_Element
*element
,
257 const DOMString tag
) const
259 IXML_Element
*child
=
260 Element_GetFirstChildByTag(element
, tag
);
262 return Element_GetTextValue(child
);
267 * \brief Returns the first child element that matches the requested tag or
270 IXML_Element
*CUPnPLib::Element_GetFirstChildByTag(
271 IXML_Element
*element
,
272 const DOMString tag
) const
274 if (!element
|| !tag
) {
278 IXML_Node
*node
= REINTERPRET_CAST(IXML_Node
*)(element
);
279 IXML_Node
*child
= ixmlNode_getFirstChild(node
);
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
= REINTERPRET_CAST(IXML_Node
*)(element
);
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(
316 REINTERPRET_CAST(IXML_Node
*)(element
));
317 IXML_Node
*attribute
= ixmlNamedNodeMap_getNamedItem(NamedNodeMap
, tag
);
318 const DOMString s
= ixmlNode_getNodeValue(attribute
);
323 ixmlNamedNodeMap_free(NamedNodeMap
);
329 CUPnPError::CUPnPError(
330 const CUPnPLib
&upnpLib
,
331 IXML_Document
*errorDoc
)
333 m_root (upnpLib
.Element_GetRootElement(errorDoc
)),
334 m_ErrorCode (upnpLib
.Element_GetChildValueByTag(m_root
, "errorCode")),
335 m_ErrorDescription(upnpLib
.Element_GetChildValueByTag(m_root
, "errorDescription"))
340 CUPnPArgument::CUPnPArgument(
341 const CUPnPControlPoint
&upnpControlPoint
,
343 IXML_Element
*argument
,
344 const std::string
&WXUNUSED(SCPDURL
))
346 m_UPnPControlPoint(upnpControlPoint
),
347 m_name (upnpLib
.Element_GetChildValueByTag(argument
, "name")),
348 m_direction (upnpLib
.Element_GetChildValueByTag(argument
, "direction")),
349 m_retval (upnpLib
.Element_GetFirstChildByTag(argument
, "retval")),
350 m_relatedStateVariable(upnpLib
.Element_GetChildValueByTag(argument
, "relatedStateVariable"))
352 std::ostringstream msg
;
353 msg
<< "\n Argument:" <<
354 "\n name: " << m_name
<<
355 "\n direction: " << m_direction
<<
356 "\n retval: " << m_retval
<<
357 "\n relatedStateVariable: " << m_relatedStateVariable
;
358 AddDebugLogLineM(false, logUPnP
, msg
);
362 CUPnPAction::CUPnPAction(
363 const CUPnPControlPoint
&upnpControlPoint
,
365 IXML_Element
*action
,
366 const std::string
&SCPDURL
)
368 m_UPnPControlPoint(upnpControlPoint
),
369 m_ArgumentList(upnpControlPoint
, upnpLib
, action
, SCPDURL
),
370 m_name(upnpLib
.Element_GetChildValueByTag(action
, "name"))
372 std::ostringstream msg
;
373 msg
<< "\n Action:" <<
374 "\n name: " << m_name
;
375 AddDebugLogLineM(false, logUPnP
, msg
);
379 CUPnPAllowedValue::CUPnPAllowedValue(
380 const CUPnPControlPoint
&upnpControlPoint
,
382 IXML_Element
*allowedValue
,
383 const std::string
&WXUNUSED(SCPDURL
))
385 m_UPnPControlPoint(upnpControlPoint
),
386 m_allowedValue(upnpLib
.Element_GetTextValue(allowedValue
))
388 std::ostringstream msg
;
389 msg
<< "\n AllowedValue:" <<
390 "\n allowedValue: " << m_allowedValue
;
391 AddDebugLogLineM(false, logUPnP
, msg
);
395 CUPnPStateVariable::CUPnPStateVariable(
396 const CUPnPControlPoint
&upnpControlPoint
,
398 IXML_Element
*stateVariable
,
399 const std::string
&SCPDURL
)
401 m_UPnPControlPoint(upnpControlPoint
),
402 m_AllowedValueList(upnpControlPoint
, upnpLib
, stateVariable
, SCPDURL
),
403 m_name (upnpLib
.Element_GetChildValueByTag(stateVariable
, "name")),
404 m_dataType (upnpLib
.Element_GetChildValueByTag(stateVariable
, "dataType")),
405 m_defaultValue(upnpLib
.Element_GetChildValueByTag(stateVariable
, "defaultValue")),
406 m_sendEvents (upnpLib
.Element_GetAttributeByTag (stateVariable
, "sendEvents"))
408 std::ostringstream msg
;
409 msg
<< "\n StateVariable:" <<
410 "\n name: " << m_name
<<
411 "\n dataType: " << m_dataType
<<
412 "\n defaultValue: " << m_defaultValue
<<
413 "\n sendEvents: " << m_sendEvents
;
414 AddDebugLogLineM(false, logUPnP
, msg
);
418 CUPnPSCPD::CUPnPSCPD(
419 const CUPnPControlPoint
&upnpControlPoint
,
422 const std::string
&SCPDURL
)
424 m_UPnPControlPoint(upnpControlPoint
),
425 m_ActionList(upnpControlPoint
, upnpLib
, scpd
, SCPDURL
),
426 m_ServiceStateTable(upnpControlPoint
, upnpLib
, scpd
, SCPDURL
),
432 CUPnPArgumentValue::CUPnPArgumentValue()
440 CUPnPArgumentValue::CUPnPArgumentValue(
441 const std::string
&argument
, const std::string
&value
)
443 m_argument(argument
),
449 CUPnPService::CUPnPService(
450 const CUPnPControlPoint
&upnpControlPoint
,
452 IXML_Element
*service
,
453 const std::string
&URLBase
)
455 m_UPnPControlPoint(upnpControlPoint
),
457 m_serviceType(upnpLib
.Element_GetChildValueByTag(service
, "serviceType")),
458 m_serviceId (upnpLib
.Element_GetChildValueByTag(service
, "serviceId")),
459 m_SCPDURL (upnpLib
.Element_GetChildValueByTag(service
, "SCPDURL")),
460 m_controlURL (upnpLib
.Element_GetChildValueByTag(service
, "controlURL")),
461 m_eventSubURL(upnpLib
.Element_GetChildValueByTag(service
, "eventSubURL")),
465 std::ostringstream msg
;
468 std::vector
<char> vscpdURL(URLBase
.length() + m_SCPDURL
.length() + 1);
469 char *scpdURL
= &vscpdURL
[0];
470 errcode
= UpnpResolveURL(
474 if( errcode
!= UPNP_E_SUCCESS
) {
475 msg
<< "Error generating scpdURL from " <<
476 "|" << URLBase
<< "|" <<
478 AddDebugLogLineM(false, logUPnP
, msg
);
480 m_absSCPDURL
= scpdURL
;
483 std::vector
<char> vcontrolURL(
484 URLBase
.length() + m_controlURL
.length() + 1);
485 char *controlURL
= &vcontrolURL
[0];
486 errcode
= UpnpResolveURL(
488 m_controlURL
.c_str(),
490 if( errcode
!= UPNP_E_SUCCESS
) {
491 msg
<< "Error generating controlURL from " <<
492 "|" << URLBase
<< "|" <<
493 m_controlURL
<< "|.";
494 AddDebugLogLineM(false, logUPnP
, msg
);
496 m_absControlURL
= controlURL
;
499 std::vector
<char> veventURL(
500 URLBase
.length() + m_eventSubURL
.length() + 1);
501 char *eventURL
= &veventURL
[0];
502 errcode
= UpnpResolveURL(
504 m_eventSubURL
.c_str(),
506 if( errcode
!= UPNP_E_SUCCESS
) {
507 msg
<< "Error generating eventURL from " <<
508 "|" << URLBase
<< "|" <<
509 m_eventSubURL
<< "|.";
510 AddDebugLogLineM(false, logUPnP
, msg
);
512 m_absEventSubURL
= eventURL
;
515 msg
<< "\n Service:" <<
516 "\n serviceType: " << m_serviceType
<<
517 "\n serviceId: " << m_serviceId
<<
518 "\n SCPDURL: " << m_SCPDURL
<<
519 "\n absSCPDURL: " << m_absSCPDURL
<<
520 "\n controlURL: " << m_controlURL
<<
521 "\n absControlURL: " << m_absControlURL
<<
522 "\n eventSubURL: " << m_eventSubURL
<<
523 "\n absEventSubURL: " << m_absEventSubURL
;
524 AddDebugLogLineM(false, logUPnP
, msg
);
526 if (m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_IP_CONNECTION
||
527 m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_PPP_CONNECTION
) {
529 m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_PPP_CONNECTION
||
530 m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG
||
531 m_serviceType
== upnpLib
.UPNP_SERVICE_LAYER3_FORWARDING
) {
534 //#warning Delete this code on release.
535 if (!upnpLib
.m_ctrlPoint
.WanServiceDetected()) {
536 // This condition can be used to suspend the parse
539 //#warning Delete this code when m_WanService is no longer used.
540 upnpLib
.m_ctrlPoint
.SetWanService(this);
543 msg
<< "WAN Service Detected: '" <<
544 m_serviceType
<< "'.";
545 AddDebugLogLineM(true, logUPnP
, msg
);
547 upnpLib
.m_ctrlPoint
.Subscribe(*this);
549 //#warning Delete this code on release.
552 msg
<< "WAN service detected again: '" <<
554 "'. Will only use the first instance.";
555 AddDebugLogLineM(true, logUPnP
, msg
);
560 msg
<< "Uninteresting service detected: '" <<
561 m_serviceType
<< "'. Ignoring.";
562 AddDebugLogLineM(true, logUPnP
, msg
);
567 CUPnPService::~CUPnPService()
572 bool CUPnPService::Execute(
573 const std::string
&ActionName
,
574 const std::vector
<CUPnPArgumentValue
> &ArgValue
) const
576 std::ostringstream msg
;
577 if (m_SCPD
.get() == NULL
) {
578 msg
<< "Service without SCPD Document, cannot execute action '" << ActionName
<<
579 "' for service '" << GetServiceType() << "'.";
580 AddDebugLogLineM(false, logUPnP
, msg
);
583 std::ostringstream
msgAction("Sending action ");
584 // Check for correct action name
585 ActionList::const_iterator itAction
=
586 m_SCPD
->GetActionList().find(ActionName
);
587 if (itAction
== m_SCPD
->GetActionList().end()) {
588 msg
<< "Invalid action name '" << ActionName
<<
589 "' for service '" << GetServiceType() << "'.";
590 AddDebugLogLineM(false, logUPnP
, msg
);
593 msgAction
<< ActionName
<< "(";
594 bool firstTime
= true;
595 // Check for correct Argument/Value pairs
596 const CUPnPAction
&action
= *(itAction
->second
);
597 for (unsigned int i
= 0; i
< ArgValue
.size(); ++i
) {
598 ArgumentList::const_iterator itArg
=
599 action
.GetArgumentList().find(ArgValue
[i
].GetArgument());
600 if (itArg
== action
.GetArgumentList().end()) {
601 msg
<< "Invalid argument name '" << ArgValue
[i
].GetArgument() <<
602 "' for action '" << action
.GetName() <<
603 "' for service '" << GetServiceType() << "'.";
604 AddDebugLogLineM(false, logUPnP
, msg
);
607 const CUPnPArgument
&argument
= *(itArg
->second
);
608 if (tolower(argument
.GetDirection()[0]) != 'i' ||
609 tolower(argument
.GetDirection()[1]) != 'n') {
610 msg
<< "Invalid direction for argument '" <<
611 ArgValue
[i
].GetArgument() <<
612 "' for action '" << action
.GetName() <<
613 "' for service '" << GetServiceType() << "'.";
614 AddDebugLogLineM(false, logUPnP
, msg
);
617 const std::string relatedStateVariableName
=
618 argument
.GetRelatedStateVariable();
619 if (!relatedStateVariableName
.empty()) {
620 ServiceStateTable::const_iterator itSVT
=
621 m_SCPD
->GetServiceStateTable().
622 find(relatedStateVariableName
);
623 if (itSVT
== m_SCPD
->GetServiceStateTable().end()) {
624 msg
<< "Inconsistent Service State Table, did not find '" <<
625 relatedStateVariableName
<<
626 "' for argument '" << argument
.GetName() <<
627 "' for action '" << action
.GetName() <<
628 "' for service '" << GetServiceType() << "'.";
629 AddDebugLogLineM(false, logUPnP
, msg
);
632 const CUPnPStateVariable
&stateVariable
= *(itSVT
->second
);
633 if ( !stateVariable
.GetAllowedValueList().empty() &&
634 stateVariable
.GetAllowedValueList().find(ArgValue
[i
].GetValue()) ==
635 stateVariable
.GetAllowedValueList().end()) {
636 msg
<< "Value not allowed '" << ArgValue
[i
].GetValue() <<
637 "' for state variable '" << relatedStateVariableName
<<
638 "' for argument '" << argument
.GetName() <<
639 "' for action '" << action
.GetName() <<
640 "' for service '" << GetServiceType() << "'.";
641 AddDebugLogLineM(false, logUPnP
, msg
);
651 ArgValue
[i
].GetArgument() <<
653 ArgValue
[i
].GetValue() <<
657 AddDebugLogLineM(false, logUPnP
, msgAction
);
658 // Everything is ok, make the action
659 IXML_Document
*ActionDoc
= NULL
;
660 if (ArgValue
.size()) {
661 for (unsigned int i
= 0; i
< ArgValue
.size(); ++i
) {
662 int ret
= UpnpAddToAction(
664 action
.GetName().c_str(),
665 GetServiceType().c_str(),
666 ArgValue
[i
].GetArgument().c_str(),
667 ArgValue
[i
].GetValue().c_str());
668 if (ret
!= UPNP_E_SUCCESS
) {
669 m_upnpLib
.processUPnPErrorMessage(
670 "UpnpAddToAction", ret
, NULL
, NULL
);
675 ActionDoc
= UpnpMakeAction(
676 action
.GetName().c_str(),
677 GetServiceType().c_str(),
680 msg
<< "Error: UpnpMakeAction returned NULL.";
681 AddLogLineU(false, logUPnP
, msg
);
686 // Send the action asynchronously
688 m_UPnPControlPoint
.GetUPnPClientHandle(),
689 GetAbsControlURL().c_str(),
690 GetServiceType().c_str(),
692 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
697 // Send the action synchronously
698 IXML_Document
*RespDoc
= NULL
;
699 int ret
= UpnpSendAction(
700 m_UPnPControlPoint
.GetUPnPClientHandle(),
701 GetAbsControlURL().c_str(),
702 GetServiceType().c_str(),
703 NULL
, ActionDoc
, &RespDoc
);
704 if (ret
!= UPNP_E_SUCCESS
) {
705 m_upnpLib
.processUPnPErrorMessage(
706 "UpnpSendAction", ret
, NULL
, RespDoc
);
707 ixmlDocument_free(ActionDoc
);
708 ixmlDocument_free(RespDoc
);
711 ixmlDocument_free(ActionDoc
);
713 // Check the response document
714 m_upnpLib
.ProcessActionResponse(
715 RespDoc
, action
.GetName());
717 // Free the response document
718 ixmlDocument_free(RespDoc
);
724 const std::string
CUPnPService::GetStateVariable(
725 const std::string
&stateVariableName
) const
727 std::ostringstream msg
;
729 int ret
= UpnpGetServiceVarStatus(
730 m_UPnPControlPoint
.GetUPnPClientHandle(),
731 GetAbsControlURL().c_str(),
732 stateVariableName
.c_str(),
734 if (ret
!= UPNP_E_SUCCESS
) {
735 msg
<< "GetStateVariable(\"" <<
737 "\"): in a call to UpnpGetServiceVarStatus";
738 m_upnpLib
.processUPnPErrorMessage(
739 msg
.str(), ret
, StVarVal
, NULL
);
740 return stdEmptyString
;
742 msg
<< "GetStateVariable: " <<
747 AddDebugLogLineM(false, logUPnP
, msg
);
748 return stdEmptyString
;
752 CUPnPDevice::CUPnPDevice(
753 const CUPnPControlPoint
&upnpControlPoint
,
755 IXML_Element
*device
,
756 const std::string
&URLBase
)
758 m_UPnPControlPoint(upnpControlPoint
),
759 m_DeviceList(upnpControlPoint
, upnpLib
, device
, URLBase
),
760 m_ServiceList(upnpControlPoint
, upnpLib
, device
, URLBase
),
761 m_deviceType (upnpLib
.Element_GetChildValueByTag(device
, "deviceType")),
762 m_friendlyName (upnpLib
.Element_GetChildValueByTag(device
, "friendlyName")),
763 m_manufacturer (upnpLib
.Element_GetChildValueByTag(device
, "manufacturer")),
764 m_manufacturerURL (upnpLib
.Element_GetChildValueByTag(device
, "manufacturerURL")),
765 m_modelDescription (upnpLib
.Element_GetChildValueByTag(device
, "modelDescription")),
766 m_modelName (upnpLib
.Element_GetChildValueByTag(device
, "modelName")),
767 m_modelNumber (upnpLib
.Element_GetChildValueByTag(device
, "modelNumber")),
768 m_modelURL (upnpLib
.Element_GetChildValueByTag(device
, "modelURL")),
769 m_serialNumber (upnpLib
.Element_GetChildValueByTag(device
, "serialNumber")),
770 m_UDN (upnpLib
.Element_GetChildValueByTag(device
, "UDN")),
771 m_UPC (upnpLib
.Element_GetChildValueByTag(device
, "UPC")),
772 m_presentationURL (upnpLib
.Element_GetChildValueByTag(device
, "presentationURL"))
774 std::ostringstream msg
;
775 int presURLlen
= strlen(URLBase
.c_str()) +
776 strlen(m_presentationURL
.c_str()) + 2;
777 std::vector
<char> vpresURL(presURLlen
);
778 char* presURL
= &vpresURL
[0];
779 int errcode
= UpnpResolveURL(
781 m_presentationURL
.c_str(),
783 if (errcode
!= UPNP_E_SUCCESS
) {
784 msg
<< "Error generating presentationURL from " <<
785 "|" << URLBase
<< "|" <<
786 m_presentationURL
<< "|.";
787 AddDebugLogLineM(false, logUPnP
, msg
);
789 m_presentationURL
= presURL
;
793 msg
<< "\n Device: " <<
794 "\n friendlyName: " << m_friendlyName
<<
795 "\n deviceType: " << m_deviceType
<<
796 "\n manufacturer: " << m_manufacturer
<<
797 "\n manufacturerURL: " << m_manufacturerURL
<<
798 "\n modelDescription: " << m_modelDescription
<<
799 "\n modelName: " << m_modelName
<<
800 "\n modelNumber: " << m_modelNumber
<<
801 "\n modelURL: " << m_modelURL
<<
802 "\n serialNumber: " << m_serialNumber
<<
803 "\n UDN: " << m_UDN
<<
804 "\n UPC: " << m_UPC
<<
805 "\n presentationURL: " << m_presentationURL
;
806 AddDebugLogLineM(false, logUPnP
, msg
);
810 CUPnPRootDevice::CUPnPRootDevice(
811 const CUPnPControlPoint
&upnpControlPoint
,
813 IXML_Element
*rootDevice
,
814 const std::string
&OriginalURLBase
,
815 const std::string
&FixedURLBase
,
816 const char *location
,
819 CUPnPDevice(upnpControlPoint
, upnpLib
, rootDevice
, FixedURLBase
),
820 m_UPnPControlPoint(upnpControlPoint
),
821 m_URLBase(OriginalURLBase
),
822 m_location(location
),
825 std::ostringstream msg
;
827 "\n Root Device: " <<
828 "\n URLBase: " << m_URLBase
<<
829 "\n Fixed URLBase: " << FixedURLBase
<<
830 "\n location: " << m_location
<<
831 "\n expires: " << m_expires
;
832 AddDebugLogLineM(false, logUPnP
, msg
);
836 CUPnPControlPoint
*CUPnPControlPoint::s_CtrlPoint
= NULL
;
839 CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort
)
842 m_UPnPClientHandle(),
845 m_ActivePortMappingsMap(),
846 m_RootDeviceListMutex(),
847 m_IGWDeviceDetected(false),
852 // Null string at first
853 std::ostringstream msg
;
857 char *ipAddress
= NULL
;
858 unsigned short port
= 0;
859 ret
= UpnpInit(ipAddress
, udpPort
);
860 if (ret
!= UPNP_E_SUCCESS
) {
861 msg
<< "error(UpnpInit): Error code ";
864 port
= UpnpGetServerPort();
865 ipAddress
= UpnpGetServerIpAddress();
866 msg
<< "bound to " << ipAddress
<< ":" <<
868 AddLogLineU(false, logUPnP
, msg
);
870 ret
= UpnpRegisterClient(
871 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
873 &m_UPnPClientHandle
);
874 if (ret
!= UPNP_E_SUCCESS
) {
875 msg
<< "error(UpnpRegisterClient): Error registering callback: ";
879 // We could ask for just the right device here. If the root device
880 // contains the device we want, it will respond with the full XML doc,
881 // including the root device and every sub-device it has.
883 // But lets find out what we have in our network by calling UPNP_ROOT_DEVICE.
885 // We should not search twice, because this will produce two
886 // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
888 ret
= UpnpSearchAsync(m_UPnPClientHandle
, 3, m_upnpLib
.UPNP_ROOT_DEVICE
.c_str(), NULL
);
889 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this);
890 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this);
891 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this);
892 if (ret
!= UPNP_E_SUCCESS
) {
893 msg
<< "error(UpnpSearchAsync): Error sending search request: ";
897 // Wait for the UPnP initialization to complete.
899 // Lock the search timeout mutex
900 m_WaitForSearchTimeoutMutex
.Lock();
902 // Lock it again, so that we block. Unlocking will only happen
903 // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
905 CUPnPMutexLocker
lock(m_WaitForSearchTimeoutMutex
);
912 msg
<< ret
<< ": " << m_upnpLib
.GetUPnPErrorMessage(ret
) << ".";
913 throw CUPnPException(msg
);
917 CUPnPControlPoint::~CUPnPControlPoint()
919 for( RootDeviceMap::iterator it
= m_RootDeviceMap
.begin();
920 it
!= m_RootDeviceMap
.end();
926 UpnpUnRegisterClient(m_UPnPClientHandle
);
931 bool CUPnPControlPoint::AddPortMappings(
932 std::vector
<CUPnPPortMapping
> &upnpPortMapping
)
934 std::ostringstream msg
;
935 if (!WanServiceDetected()) {
936 msg
<< "UPnP Error: "
937 "CUPnPControlPoint::AddPortMapping: "
938 "WAN Service not detected.";
939 AddLogLineU(true, logUPnP
, msg
);
943 int n
= upnpPortMapping
.size();
946 // Check the number of port mappings before
947 std::istringstream
PortMappingNumberOfEntries(
948 m_WanService
->GetStateVariable(
949 "PortMappingNumberOfEntries"));
950 unsigned long oldNumberOfEntries
;
951 PortMappingNumberOfEntries
>> oldNumberOfEntries
;
953 // Add the enabled port mappings
954 for (int i
= 0; i
< n
; ++i
) {
955 if (upnpPortMapping
[i
].getEnabled() == "1") {
956 // Add the mapping to the control point
957 // active mappings list
958 m_ActivePortMappingsMap
[upnpPortMapping
[i
].getKey()] =
961 // Add the port mapping
962 PrivateAddPortMapping(upnpPortMapping
[i
]);
966 // Test some variables, this is deprecated, might not work
968 m_WanService
->GetStateVariable("ConnectionType");
969 m_WanService
->GetStateVariable("PossibleConnectionTypes");
970 m_WanService
->GetStateVariable("ConnectionStatus");
971 m_WanService
->GetStateVariable("Uptime");
972 m_WanService
->GetStateVariable("LastConnectionError");
973 m_WanService
->GetStateVariable("RSIPAvailable");
974 m_WanService
->GetStateVariable("NATEnabled");
975 m_WanService
->GetStateVariable("ExternalIPAddress");
976 m_WanService
->GetStateVariable("PortMappingNumberOfEntries");
977 m_WanService
->GetStateVariable("PortMappingLeaseDuration");
980 std::vector
<CUPnPArgumentValue
> argval
;
982 m_WanService
->Execute("GetStatusInfo", argval
);
985 // These do not work. Their value must be requested for a
986 // specific port mapping.
987 m_WanService
->GetStateVariable("PortMappingEnabled");
988 m_WanService
->GetStateVariable("RemoteHost");
989 m_WanService
->GetStateVariable("ExternalPort");
990 m_WanService
->GetStateVariable("InternalPort");
991 m_WanService
->GetStateVariable("PortMappingProtocol");
992 m_WanService
->GetStateVariable("InternalClient");
993 m_WanService
->GetStateVariable("PortMappingDescription");
998 msg
<< "CUPnPControlPoint::DeletePortMappings: "
999 "m_ActivePortMappingsMap.size() == " <<
1000 m_ActivePortMappingsMap
.size();
1001 AddDebugLogLineM(false, logUPnP
, msg
);
1003 // Not very good, must find a better test
1004 PortMappingNumberOfEntries
.str(
1005 m_WanService
->GetStateVariable(
1006 "PortMappingNumberOfEntries"));
1007 unsigned long newNumberOfEntries
;
1008 PortMappingNumberOfEntries
>> newNumberOfEntries
;
1009 ok
= newNumberOfEntries
- oldNumberOfEntries
== 4;
1015 void CUPnPControlPoint::RefreshPortMappings()
1017 for ( PortMappingMap::iterator it
= m_ActivePortMappingsMap
.begin();
1018 it
!= m_ActivePortMappingsMap
.end();
1020 PrivateAddPortMapping(it
->second
);
1024 m_WanService
->GetStateVariable("PortMappingNumberOfEntries");
1028 bool CUPnPControlPoint::PrivateAddPortMapping(
1029 CUPnPPortMapping
&upnpPortMapping
)
1031 // Get an IP address. The UPnP server one must do.
1032 std::string
ipAddress(UpnpGetServerIpAddress());
1034 // Start building the action
1035 std::string
actionName("AddPortMapping");
1036 std::vector
<CUPnPArgumentValue
> argval(8);
1038 // Action parameters
1039 argval
[0].SetArgument("NewRemoteHost");
1040 argval
[0].SetValue("");
1041 argval
[1].SetArgument("NewExternalPort");
1042 argval
[1].SetValue(upnpPortMapping
.getPort());
1043 argval
[2].SetArgument("NewProtocol");
1044 argval
[2].SetValue(upnpPortMapping
.getProtocol());
1045 argval
[3].SetArgument("NewInternalPort");
1046 argval
[3].SetValue(upnpPortMapping
.getPort());
1047 argval
[4].SetArgument("NewInternalClient");
1048 argval
[4].SetValue(ipAddress
);
1049 argval
[5].SetArgument("NewEnabled");
1050 argval
[5].SetValue("1");
1051 argval
[6].SetArgument("NewPortMappingDescription");
1052 argval
[6].SetValue(upnpPortMapping
.getDescription());
1053 argval
[7].SetArgument("NewLeaseDuration");
1054 argval
[7].SetValue("0");
1058 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1059 it
!= m_ServiceMap
.end(); ++it
) {
1060 ret
&= it
->second
->Execute(actionName
, argval
);
1067 bool CUPnPControlPoint::DeletePortMappings(
1068 std::vector
<CUPnPPortMapping
> &upnpPortMapping
)
1070 std::ostringstream msg
;
1071 if (!WanServiceDetected()) {
1072 msg
<< "UPnP Error: "
1073 "CUPnPControlPoint::DeletePortMapping: "
1074 "WAN Service not detected.";
1075 AddLogLineU(true, logUPnP
, msg
);
1079 int n
= upnpPortMapping
.size();
1082 // Check the number of port mappings before
1083 std::istringstream
PortMappingNumberOfEntries(
1084 m_WanService
->GetStateVariable(
1085 "PortMappingNumberOfEntries"));
1086 unsigned long oldNumberOfEntries
;
1087 PortMappingNumberOfEntries
>> oldNumberOfEntries
;
1089 // Delete the enabled port mappings
1090 for (int i
= 0; i
< n
; ++i
) {
1091 if (upnpPortMapping
[i
].getEnabled() == "1") {
1092 // Delete the mapping from the control point
1093 // active mappings list
1094 PortMappingMap::iterator it
=
1095 m_ActivePortMappingsMap
.find(
1096 upnpPortMapping
[i
].getKey());
1097 if (it
!= m_ActivePortMappingsMap
.end()) {
1098 m_ActivePortMappingsMap
.erase(it
);
1100 msg
<< "UPnP Error: "
1101 "CUPnPControlPoint::DeletePortMapping: "
1102 "Mapping was not found in the active "
1104 AddLogLineU(true, logUPnP
, msg
);
1107 // Delete the port mapping
1108 PrivateDeletePortMapping(upnpPortMapping
[i
]);
1114 msg
<< "CUPnPControlPoint::DeletePortMappings: "
1115 "m_ActivePortMappingsMap.size() == " <<
1116 m_ActivePortMappingsMap
.size();
1117 AddDebugLogLineM(false, logUPnP
, msg
);
1119 // Not very good, must find a better test
1120 PortMappingNumberOfEntries
.str(
1121 m_WanService
->GetStateVariable(
1122 "PortMappingNumberOfEntries"));
1123 unsigned long newNumberOfEntries
;
1124 PortMappingNumberOfEntries
>> newNumberOfEntries
;
1125 ok
= oldNumberOfEntries
- newNumberOfEntries
== 4;
1131 bool CUPnPControlPoint::PrivateDeletePortMapping(
1132 CUPnPPortMapping
&upnpPortMapping
)
1134 // Start building the action
1135 std::string
actionName("DeletePortMapping");
1136 std::vector
<CUPnPArgumentValue
> argval(3);
1138 // Action parameters
1139 argval
[0].SetArgument("NewRemoteHost");
1140 argval
[0].SetValue("");
1141 argval
[1].SetArgument("NewExternalPort");
1142 argval
[1].SetValue(upnpPortMapping
.getPort());
1143 argval
[2].SetArgument("NewProtocol");
1144 argval
[2].SetValue(upnpPortMapping
.getProtocol());
1148 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1149 it
!= m_ServiceMap
.end(); ++it
) {
1150 ret
&= it
->second
->Execute(actionName
, argval
);
1157 // This function is static
1158 int CUPnPControlPoint::Callback(Upnp_EventType EventType
, void *Event
, void * /*Cookie*/)
1160 std::ostringstream msg
;
1161 std::ostringstream msg2
;
1162 // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
1163 // happen with a wrong cookie and... boom!
1164 // CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
1165 CUPnPControlPoint
*upnpCP
= CUPnPControlPoint::s_CtrlPoint
;
1167 //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
1168 switch (EventType
) {
1169 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
:
1170 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
1171 msg
<< "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): ";
1172 msg2
<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
1174 case UPNP_DISCOVERY_SEARCH_RESULT
: {
1175 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n");
1176 msg
<< "error(UPNP_DISCOVERY_SEARCH_RESULT): ";
1177 msg2
<< "UPNP_DISCOVERY_SEARCH_RESULT: ";
1180 struct Upnp_Discovery
*d_event
= (struct Upnp_Discovery
*)Event
;
1181 IXML_Document
*doc
= NULL
;
1183 if (d_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1184 msg
<< upnpCP
->m_upnpLib
.GetUPnPErrorMessage(d_event
->ErrCode
) << ".";
1185 AddDebugLogLineM(true, logUPnP
, msg
);
1187 // Get the XML tree device description in doc
1188 ret
= UpnpDownloadXmlDoc(d_event
->Location
, &doc
);
1189 if (ret
!= UPNP_E_SUCCESS
) {
1190 msg
<< "Error retrieving device description from " <<
1191 d_event
->Location
<< ": " <<
1192 upnpCP
->m_upnpLib
.GetUPnPErrorMessage(ret
) <<
1194 AddDebugLogLineM(true, logUPnP
, msg
);
1196 msg2
<< "Retrieving device description from " <<
1197 d_event
->Location
<< ".";
1198 AddDebugLogLineM(false, logUPnP
, msg2
);
1201 // Get the root node
1202 IXML_Element
*root
=
1203 upnpCP
->m_upnpLib
.Element_GetRootElement(doc
);
1204 // Extract the URLBase
1205 const std::string urlBase
= upnpCP
->m_upnpLib
.
1206 Element_GetChildValueByTag(root
, "URLBase");
1207 // Get the root device
1208 IXML_Element
*rootDevice
= upnpCP
->m_upnpLib
.
1209 Element_GetFirstChildByTag(root
, "device");
1210 // Extract the deviceType
1211 std::string
devType(upnpCP
->m_upnpLib
.
1212 Element_GetChildValueByTag(rootDevice
, "deviceType"));
1213 // Only add device if it is an InternetGatewayDevice
1214 if (stdStringIsEqualCI(devType
, upnpCP
->m_upnpLib
.UPNP_DEVICE_IGW
)) {
1215 // This condition can be used to auto-detect
1216 // the UPnP device we are interested in.
1217 // Obs.: Don't block the entry here on this
1218 // condition! There may be more than one device,
1219 // and the first that enters may not be the one
1220 // we are interested in!
1221 upnpCP
->SetIGWDeviceDetected(true);
1222 // Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
1223 // we don't want to spam our logs.
1224 if (EventType
!= UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
) {
1225 msg
.str("Internet Gateway Device Detected.");
1226 AddLogLineU(true, logUPnP
, msg
);
1228 // Add the root device to our list
1229 upnpCP
->AddRootDevice(rootDevice
, urlBase
,
1230 d_event
->Location
, d_event
->Expires
);
1232 // Free the XML doc tree
1233 ixmlDocument_free(doc
);
1237 case UPNP_DISCOVERY_SEARCH_TIMEOUT
: {
1238 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1240 msg
<< "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1241 AddDebugLogLineM(false, logUPnP
, msg
);
1243 // Unlock the search timeout mutex
1244 upnpCP
->m_WaitForSearchTimeoutMutex
.Unlock();
1248 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE
: {
1249 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
1250 // UPnP Device Removed
1251 struct Upnp_Discovery
*dab_event
= (struct Upnp_Discovery
*)Event
;
1252 if (dab_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1253 msg
<< "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
1254 upnpCP
->m_upnpLib
.GetUPnPErrorMessage(dab_event
->ErrCode
) <<
1256 AddDebugLogLineM(true, logUPnP
, msg
);
1258 std::string devType
= dab_event
->DeviceType
;
1259 // Check for an InternetGatewayDevice and removes it from the list
1260 std::transform(devType
.begin(), devType
.end(), devType
.begin(), tolower
);
1261 if (stdStringIsEqualCI(devType
, upnpCP
->m_upnpLib
.UPNP_DEVICE_IGW
)) {
1262 upnpCP
->RemoveRootDevice(dab_event
->DeviceId
);
1266 case UPNP_EVENT_RECEIVED
: {
1267 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1269 struct Upnp_Event
*e_event
= (struct Upnp_Event
*)Event
;
1270 const std::string Sid
= e_event
->Sid
;
1272 upnpCP
->OnEventReceived(Sid
, e_event
->EventKey
, e_event
->ChangedVariables
);
1275 case UPNP_EVENT_SUBSCRIBE_COMPLETE
:
1276 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
1277 msg
<< "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): ";
1278 goto upnpEventRenewalComplete
;
1279 case UPNP_EVENT_UNSUBSCRIBE_COMPLETE
:
1280 //fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
1281 msg
<< "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): ";
1282 goto upnpEventRenewalComplete
;
1283 case UPNP_EVENT_RENEWAL_COMPLETE
: {
1284 //fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n");
1285 msg
<< "error(UPNP_EVENT_RENEWAL_COMPLETE): ";
1286 upnpEventRenewalComplete
:
1287 struct Upnp_Event_Subscribe
*es_event
=
1288 (struct Upnp_Event_Subscribe
*)Event
;
1289 if (es_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1290 msg
<< "Error in Event Subscribe Callback";
1291 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1292 msg
.str(), es_event
->ErrCode
, NULL
, NULL
);
1295 TvCtrlPointHandleSubscribeUpdate(
1296 es_event
->PublisherUrl
,
1298 es_event
->TimeOut
);
1305 case UPNP_EVENT_AUTORENEWAL_FAILED
:
1306 //fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n");
1307 msg
<< "error(UPNP_EVENT_AUTORENEWAL_FAILED): ";
1308 msg2
<< "UPNP_EVENT_AUTORENEWAL_FAILED: ";
1309 goto upnpEventSubscriptionExpired
;
1310 case UPNP_EVENT_SUBSCRIPTION_EXPIRED
: {
1311 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
1312 msg
<< "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): ";
1313 msg2
<< "UPNP_EVENT_SUBSCRIPTION_EXPIRED: ";
1314 upnpEventSubscriptionExpired
:
1315 struct Upnp_Event_Subscribe
*es_event
=
1316 (struct Upnp_Event_Subscribe
*)Event
;
1319 int ret
= UpnpSubscribe(
1320 upnpCP
->m_UPnPClientHandle
,
1321 es_event
->PublisherUrl
,
1324 if (ret
!= UPNP_E_SUCCESS
) {
1325 msg
<< "Error Subscribing to EventURL";
1326 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1327 msg
.str(), es_event
->ErrCode
, NULL
, NULL
);
1329 ServiceMap::iterator it
=
1330 upnpCP
->m_ServiceMap
.find(es_event
->PublisherUrl
);
1331 if (it
!= upnpCP
->m_ServiceMap
.end()) {
1332 CUPnPService
&service
= *(it
->second
);
1333 service
.SetTimeout(TimeOut
);
1334 service
.SetSID(newSID
);
1335 msg2
<< "Re-subscribed to EventURL '" <<
1336 es_event
->PublisherUrl
<<
1337 "' with SID == '" <<
1339 AddDebugLogLineM(true, logUPnP
, msg2
);
1340 // In principle, we should test to see if the
1341 // service is the same. But here we only have one
1343 upnpCP
->RefreshPortMappings();
1345 msg
<< "Error: did not find service " <<
1346 newSID
<< " in the service map.";
1347 AddDebugLogLineM(true, logUPnP
, msg
);
1352 case UPNP_CONTROL_ACTION_COMPLETE
: {
1353 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n");
1354 // This is here if we choose to do this asynchronously
1355 struct Upnp_Action_Complete
*a_event
=
1356 (struct Upnp_Action_Complete
*)Event
;
1357 if (a_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1358 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1359 "UpnpSendActionAsync",
1360 a_event
->ErrCode
, NULL
,
1361 a_event
->ActionResult
);
1363 // Check the response document
1364 upnpCP
->m_upnpLib
.ProcessActionResponse(
1365 a_event
->ActionResult
,
1366 "<UpnpSendActionAsync>");
1368 /* No need for any processing here, just print out results.
1369 * Service state table updates are handled by events.
1373 case UPNP_CONTROL_GET_VAR_COMPLETE
: {
1374 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n");
1375 msg
<< "error(UPNP_CONTROL_GET_VAR_COMPLETE): ";
1376 struct Upnp_State_Var_Complete
*sv_event
=
1377 (struct Upnp_State_Var_Complete
*)Event
;
1378 if (sv_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1379 msg
<< "m_UpnpGetServiceVarStatusAsync";
1380 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1381 msg
.str(), sv_event
->ErrCode
, NULL
, NULL
);
1384 // Warning: The use of UpnpGetServiceVarStatus and
1385 // UpnpGetServiceVarStatusAsync is deprecated by the
1387 TvCtrlPointHandleGetVar(
1389 sv_event
->StateVarName
,
1390 sv_event
->CurrentVal
);
1395 // ignore these cases, since this is not a device
1396 case UPNP_CONTROL_GET_VAR_REQUEST
:
1397 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n");
1398 msg
<< "error(UPNP_CONTROL_GET_VAR_REQUEST): ";
1399 goto eventSubscriptionRequest
;
1400 case UPNP_CONTROL_ACTION_REQUEST
:
1401 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n");
1402 msg
<< "error(UPNP_CONTROL_ACTION_REQUEST): ";
1403 goto eventSubscriptionRequest
;
1404 case UPNP_EVENT_SUBSCRIPTION_REQUEST
:
1405 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
1406 msg
<< "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): ";
1407 eventSubscriptionRequest
:
1408 msg
<< "This is not a UPnP Device, this is a UPnP Control Point, event ignored.";
1409 AddDebugLogLineM(true, logUPnP
, msg
);
1412 // Humm, this is not good, we forgot to handle something...
1414 "Callback: default... Unknown event:'%d', not good.\n",
1416 msg
<< "error(UPnP::Callback): Event not handled:'" <<
1418 fprintf(stderr
, "%s\n", msg
.str().c_str());
1419 AddDebugLogLineM(true, logUPnP
, msg
);
1420 // Better not throw in the callback. Who would catch it?
1421 //throw CUPnPException(msg);
1429 void CUPnPControlPoint::OnEventReceived(
1430 const std::string
&Sid
,
1432 IXML_Document
*ChangedVariablesDoc
)
1434 std::ostringstream msg
;
1435 msg
<< "UPNP_EVENT_RECEIVED:" <<
1436 "\n SID: " << Sid
<<
1437 "\n Key: " << EventKey
<<
1438 "\n Property list:";
1439 IXML_Element
*root
=
1440 m_upnpLib
.Element_GetRootElement(ChangedVariablesDoc
);
1441 IXML_Element
*child
=
1442 m_upnpLib
.Element_GetFirstChild(root
);
1445 IXML_Element
*child2
=
1446 m_upnpLib
.Element_GetFirstChild(child
);
1447 const DOMString childTag
=
1448 m_upnpLib
.Element_GetTag(child2
);
1449 std::string childValue
=
1450 m_upnpLib
.Element_GetTextValue(child2
);
1454 child
= m_upnpLib
.Element_GetNextSibling(child
);
1457 msg
<< "\n Empty property list.";
1459 AddDebugLogLineM(true, logUPnP
, msg
);
1460 // Freeing that doc segfaults. Probably should not be freed.
1461 //ixmlDocument_free(ChangedVariablesDoc);
1465 void CUPnPControlPoint::AddRootDevice(
1466 IXML_Element
*rootDevice
, const std::string
&urlBase
,
1467 const char *location
, int expires
)
1469 // Lock the Root Device List
1470 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1472 // Root node's URLBase
1473 std::string
OriginalURLBase(urlBase
);
1474 std::string
FixedURLBase(OriginalURLBase
.empty() ?
1478 // Get the UDN (Unique Device Name)
1480 m_upnpLib
.Element_GetChildValueByTag(rootDevice
, "UDN"));
1481 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1482 bool alreadyAdded
= it
!= m_RootDeviceMap
.end();
1484 // Just set the expires field
1485 it
->second
->SetExpires(expires
);
1487 // Add a new root device to the root device list
1488 CUPnPRootDevice
*upnpRootDevice
= new CUPnPRootDevice(
1489 *this, m_upnpLib
, rootDevice
,
1490 OriginalURLBase
, FixedURLBase
,
1492 m_RootDeviceMap
[upnpRootDevice
->GetUDN()] = upnpRootDevice
;
1497 void CUPnPControlPoint::RemoveRootDevice(const char *udn
)
1499 // Lock the Root Device List
1500 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1503 std::string
UDN(udn
);
1504 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1505 if (it
!= m_RootDeviceMap
.end()) {
1507 m_RootDeviceMap
.erase(UDN
);
1512 void CUPnPControlPoint::Subscribe(CUPnPService
&service
)
1514 std::ostringstream msg
;
1516 IXML_Document
*scpdDoc
= NULL
;
1517 int errcode
= UpnpDownloadXmlDoc(
1518 service
.GetAbsSCPDURL().c_str(), &scpdDoc
);
1519 if (errcode
== UPNP_E_SUCCESS
) {
1520 // Get the root node of this service (the SCPD Document)
1521 IXML_Element
*scpdRoot
=
1522 m_upnpLib
.Element_GetRootElement(scpdDoc
);
1523 CUPnPSCPD
*scpd
= new CUPnPSCPD(*this, m_upnpLib
,
1524 scpdRoot
, service
.GetAbsSCPDURL());
1525 service
.SetSCPD(scpd
);
1526 m_ServiceMap
[service
.GetAbsEventSubURL()] = &service
;
1527 msg
<< "Successfully retrieved SCPD Document for service " <<
1528 service
.GetServiceType() << ", absEventSubURL: " <<
1529 service
.GetAbsEventSubURL() << ".";
1530 AddLogLineU(true, logUPnP
, msg
);
1533 // Now try to subscribe to this service. If the subscription
1534 // is not successfull, we will not be notified about events,
1535 // but it may be possible to use the service anyway.
1536 errcode
= UpnpSubscribe(m_UPnPClientHandle
,
1537 service
.GetAbsEventSubURL().c_str(),
1538 service
.GetTimeoutAddr(),
1540 if (errcode
== UPNP_E_SUCCESS
) {
1541 msg
<< "Successfully subscribed to service " <<
1542 service
.GetServiceType() << ", absEventSubURL: " <<
1543 service
.GetAbsEventSubURL() << ".";
1544 AddLogLineU(true, logUPnP
, msg
);
1546 msg
<< "Error subscribing to service " <<
1547 service
.GetServiceType() << ", absEventSubURL: " <<
1548 service
.GetAbsEventSubURL() << ", error: " <<
1549 m_upnpLib
.GetUPnPErrorMessage(errcode
) << ".";
1553 msg
<< "Error getting SCPD Document from " <<
1554 service
.GetAbsSCPDURL() << ".";
1555 AddLogLineU(true, logUPnP
, msg
);
1562 AddLogLineU(true, logUPnP
, msg
);
1566 void CUPnPControlPoint::Unsubscribe(CUPnPService
&service
)
1568 ServiceMap::iterator it
= m_ServiceMap
.find(service
.GetAbsEventSubURL());
1569 if (it
!= m_ServiceMap
.end()) {
1570 m_ServiceMap
.erase(it
);
1571 UpnpUnSubscribe(m_UPnPClientHandle
, service
.GetSID());
1576 // File_checked_for_headers