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
27 # include "config.h" // Needed for ENABLE_UPNP
37 #include <dlfcn.h> // For dlopen(), dlsym(), dlclose()
38 #include <algorithm> // For transform()
43 #define REINTERPRET_CAST(x) reinterpret_cast<x>
46 #ifndef REINTERPRET_CAST
47 // Let's hope that function pointers are equal in size to data pointers
48 #define REINTERPRET_CAST(x) (x)
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(
192 REINTERPRET_CAST(IXML_Node
*)(doc
)));
199 * \brief Returns the first child of a given element.
201 IXML_Element
*CUPnPLib::Element_GetFirstChild(
202 IXML_Element
*parent
) const
204 IXML_Node
*node
= REINTERPRET_CAST(IXML_Node
*)(parent
);
205 IXML_Node
*child
= ixmlNode_getFirstChild(node
);
207 return REINTERPRET_CAST(IXML_Element
*)(child
);
212 * \brief Returns the next sibling of a given child.
214 IXML_Element
*CUPnPLib::Element_GetNextSibling(
215 IXML_Element
*child
) const
217 IXML_Node
*node
= REINTERPRET_CAST(IXML_Node
*)(child
);
218 IXML_Node
*sibling
= ixmlNode_getNextSibling(node
);
220 return REINTERPRET_CAST(IXML_Element
*)(sibling
);
225 * \brief Returns the element tag (name)
227 const DOMString
CUPnPLib::Element_GetTag(
228 IXML_Element
*element
) const
230 IXML_Node
*node
= REINTERPRET_CAST(IXML_Node
*)(element
);
231 const DOMString tag
= ixmlNode_getNodeName(node
);
238 * \brief Returns the TEXT node value of the current node.
240 const std::string
CUPnPLib::Element_GetTextValue(
241 IXML_Element
*element
) const
244 return stdEmptyString
;
246 IXML_Node
*text
= ixmlNode_getFirstChild(
247 REINTERPRET_CAST(IXML_Node
*)(element
));
248 const DOMString s
= ixmlNode_getNodeValue(text
);
259 * \brief Returns the TEXT node value of the first child matching tag.
261 const std::string
CUPnPLib::Element_GetChildValueByTag(
262 IXML_Element
*element
,
263 const DOMString tag
) const
265 IXML_Element
*child
=
266 Element_GetFirstChildByTag(element
, tag
);
268 return Element_GetTextValue(child
);
273 * \brief Returns the first child element that matches the requested tag or
276 IXML_Element
*CUPnPLib::Element_GetFirstChildByTag(
277 IXML_Element
*element
,
278 const DOMString tag
) const
280 if (!element
|| !tag
) {
284 IXML_Node
*node
= REINTERPRET_CAST(IXML_Node
*)(element
);
285 IXML_Node
*child
= ixmlNode_getFirstChild(node
);
286 const DOMString childTag
= ixmlNode_getNodeName(child
);
287 while(child
&& childTag
&& strcmp(tag
, childTag
)) {
288 child
= ixmlNode_getNextSibling(child
);
289 childTag
= ixmlNode_getNodeName(child
);
292 return REINTERPRET_CAST(IXML_Element
*)(child
);
297 * \brief Returns the next sibling element that matches the requested tag. Should be
298 * used with the return value of Element_GetFirstChildByTag().
300 IXML_Element
*CUPnPLib::Element_GetNextSiblingByTag(
301 IXML_Element
*element
, const DOMString tag
) const
303 if (!element
|| !tag
) {
307 IXML_Node
*child
= REINTERPRET_CAST(IXML_Node
*)(element
);
308 const DOMString childTag
= NULL
;
310 child
= ixmlNode_getNextSibling(child
);
311 childTag
= ixmlNode_getNodeName(child
);
312 } while(child
&& childTag
&& strcmp(tag
, childTag
));
314 return REINTERPRET_CAST(IXML_Element
*)(child
);
318 const std::string
CUPnPLib::Element_GetAttributeByTag(
319 IXML_Element
*element
, const DOMString tag
) const
321 IXML_NamedNodeMap
*NamedNodeMap
= ixmlNode_getAttributes(
322 REINTERPRET_CAST(IXML_Node
*)(element
));
323 IXML_Node
*attribute
= ixmlNamedNodeMap_getNamedItem(NamedNodeMap
, tag
);
324 const DOMString s
= ixmlNode_getNodeValue(attribute
);
329 ixmlNamedNodeMap_free(NamedNodeMap
);
335 CUPnPError::CUPnPError(
336 const CUPnPLib
&upnpLib
,
337 IXML_Document
*errorDoc
)
339 m_root (upnpLib
.Element_GetRootElement(errorDoc
)),
340 m_ErrorCode (upnpLib
.Element_GetChildValueByTag(m_root
, "errorCode")),
341 m_ErrorDescription(upnpLib
.Element_GetChildValueByTag(m_root
, "errorDescription"))
346 CUPnPArgument::CUPnPArgument(
347 const CUPnPControlPoint
&upnpControlPoint
,
349 IXML_Element
*argument
,
350 const std::string
&WXUNUSED(SCPDURL
))
352 m_UPnPControlPoint(upnpControlPoint
),
353 m_name (upnpLib
.Element_GetChildValueByTag(argument
, "name")),
354 m_direction (upnpLib
.Element_GetChildValueByTag(argument
, "direction")),
355 m_retval (upnpLib
.Element_GetFirstChildByTag(argument
, "retval")),
356 m_relatedStateVariable(upnpLib
.Element_GetChildValueByTag(argument
, "relatedStateVariable"))
358 std::ostringstream msg
;
359 msg
<< "\n Argument:" <<
360 "\n name: " << m_name
<<
361 "\n direction: " << m_direction
<<
362 "\n retval: " << m_retval
<<
363 "\n relatedStateVariable: " << m_relatedStateVariable
;
364 AddDebugLogLineN(logUPnP
, msg
);
368 CUPnPAction::CUPnPAction(
369 const CUPnPControlPoint
&upnpControlPoint
,
371 IXML_Element
*action
,
372 const std::string
&SCPDURL
)
374 m_UPnPControlPoint(upnpControlPoint
),
375 m_ArgumentList(upnpControlPoint
, upnpLib
, action
, SCPDURL
),
376 m_name(upnpLib
.Element_GetChildValueByTag(action
, "name"))
378 std::ostringstream msg
;
379 msg
<< "\n Action:" <<
380 "\n name: " << m_name
;
381 AddDebugLogLineN(logUPnP
, msg
);
385 CUPnPAllowedValue::CUPnPAllowedValue(
386 const CUPnPControlPoint
&upnpControlPoint
,
388 IXML_Element
*allowedValue
,
389 const std::string
&WXUNUSED(SCPDURL
))
391 m_UPnPControlPoint(upnpControlPoint
),
392 m_allowedValue(upnpLib
.Element_GetTextValue(allowedValue
))
394 std::ostringstream msg
;
395 msg
<< "\n AllowedValue:" <<
396 "\n allowedValue: " << m_allowedValue
;
397 AddDebugLogLineN(logUPnP
, msg
);
401 CUPnPStateVariable::CUPnPStateVariable(
402 const CUPnPControlPoint
&upnpControlPoint
,
404 IXML_Element
*stateVariable
,
405 const std::string
&SCPDURL
)
407 m_UPnPControlPoint(upnpControlPoint
),
408 m_AllowedValueList(upnpControlPoint
, upnpLib
, stateVariable
, SCPDURL
),
409 m_name (upnpLib
.Element_GetChildValueByTag(stateVariable
, "name")),
410 m_dataType (upnpLib
.Element_GetChildValueByTag(stateVariable
, "dataType")),
411 m_defaultValue(upnpLib
.Element_GetChildValueByTag(stateVariable
, "defaultValue")),
412 m_sendEvents (upnpLib
.Element_GetAttributeByTag (stateVariable
, "sendEvents"))
414 std::ostringstream msg
;
415 msg
<< "\n StateVariable:" <<
416 "\n name: " << m_name
<<
417 "\n dataType: " << m_dataType
<<
418 "\n defaultValue: " << m_defaultValue
<<
419 "\n sendEvents: " << m_sendEvents
;
420 AddDebugLogLineN(logUPnP
, msg
);
424 CUPnPSCPD::CUPnPSCPD(
425 const CUPnPControlPoint
&upnpControlPoint
,
428 const std::string
&SCPDURL
)
430 m_UPnPControlPoint(upnpControlPoint
),
431 m_ActionList(upnpControlPoint
, upnpLib
, scpd
, SCPDURL
),
432 m_ServiceStateTable(upnpControlPoint
, upnpLib
, scpd
, SCPDURL
),
438 CUPnPArgumentValue::CUPnPArgumentValue()
446 CUPnPArgumentValue::CUPnPArgumentValue(
447 const std::string
&argument
, const std::string
&value
)
449 m_argument(argument
),
455 CUPnPService::CUPnPService(
456 const CUPnPControlPoint
&upnpControlPoint
,
458 IXML_Element
*service
,
459 const std::string
&URLBase
)
461 m_UPnPControlPoint(upnpControlPoint
),
463 m_serviceType(upnpLib
.Element_GetChildValueByTag(service
, "serviceType")),
464 m_serviceId (upnpLib
.Element_GetChildValueByTag(service
, "serviceId")),
465 m_SCPDURL (upnpLib
.Element_GetChildValueByTag(service
, "SCPDURL")),
466 m_controlURL (upnpLib
.Element_GetChildValueByTag(service
, "controlURL")),
467 m_eventSubURL(upnpLib
.Element_GetChildValueByTag(service
, "eventSubURL")),
471 std::ostringstream msg
;
474 std::vector
<char> vscpdURL(URLBase
.length() + m_SCPDURL
.length() + 1);
475 char *scpdURL
= &vscpdURL
[0];
476 errcode
= UpnpResolveURL(
480 if( errcode
!= UPNP_E_SUCCESS
) {
481 msg
<< "Error generating scpdURL from " <<
482 "|" << URLBase
<< "|" <<
484 AddDebugLogLineN(logUPnP
, msg
);
486 m_absSCPDURL
= scpdURL
;
489 std::vector
<char> vcontrolURL(
490 URLBase
.length() + m_controlURL
.length() + 1);
491 char *controlURL
= &vcontrolURL
[0];
492 errcode
= UpnpResolveURL(
494 m_controlURL
.c_str(),
496 if( errcode
!= UPNP_E_SUCCESS
) {
497 msg
<< "Error generating controlURL from " <<
498 "|" << URLBase
<< "|" <<
499 m_controlURL
<< "|.";
500 AddDebugLogLineN(logUPnP
, msg
);
502 m_absControlURL
= controlURL
;
505 std::vector
<char> veventURL(
506 URLBase
.length() + m_eventSubURL
.length() + 1);
507 char *eventURL
= &veventURL
[0];
508 errcode
= UpnpResolveURL(
510 m_eventSubURL
.c_str(),
512 if( errcode
!= UPNP_E_SUCCESS
) {
513 msg
<< "Error generating eventURL from " <<
514 "|" << URLBase
<< "|" <<
515 m_eventSubURL
<< "|.";
516 AddDebugLogLineN(logUPnP
, msg
);
518 m_absEventSubURL
= eventURL
;
521 msg
<< "\n Service:" <<
522 "\n serviceType: " << m_serviceType
<<
523 "\n serviceId: " << m_serviceId
<<
524 "\n SCPDURL: " << m_SCPDURL
<<
525 "\n absSCPDURL: " << m_absSCPDURL
<<
526 "\n controlURL: " << m_controlURL
<<
527 "\n absControlURL: " << m_absControlURL
<<
528 "\n eventSubURL: " << m_eventSubURL
<<
529 "\n absEventSubURL: " << m_absEventSubURL
;
530 AddDebugLogLineN(logUPnP
, msg
);
532 if (m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_IP_CONNECTION
||
533 m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_PPP_CONNECTION
) {
535 m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_PPP_CONNECTION
||
536 m_serviceType
== upnpLib
.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG
||
537 m_serviceType
== upnpLib
.UPNP_SERVICE_LAYER3_FORWARDING
) {
540 //#warning Delete this code on release.
541 if (!upnpLib
.m_ctrlPoint
.WanServiceDetected()) {
542 // This condition can be used to suspend the parse
545 //#warning Delete this code when m_WanService is no longer used.
546 upnpLib
.m_ctrlPoint
.SetWanService(this);
549 msg
<< "WAN Service Detected: '" <<
550 m_serviceType
<< "'.";
551 AddDebugLogLineC(logUPnP
, msg
);
553 upnpLib
.m_ctrlPoint
.Subscribe(*this);
555 //#warning Delete this code on release.
558 msg
<< "WAN service detected again: '" <<
560 "'. Will only use the first instance.";
561 AddDebugLogLineC(logUPnP
, msg
);
566 msg
<< "Uninteresting service detected: '" <<
567 m_serviceType
<< "'. Ignoring.";
568 AddDebugLogLineC(logUPnP
, msg
);
573 CUPnPService::~CUPnPService()
578 bool CUPnPService::Execute(
579 const std::string
&ActionName
,
580 const std::vector
<CUPnPArgumentValue
> &ArgValue
) const
582 std::ostringstream msg
;
583 if (m_SCPD
.get() == NULL
) {
584 msg
<< "Service without SCPD Document, cannot execute action '" << ActionName
<<
585 "' for service '" << GetServiceType() << "'.";
586 AddDebugLogLineN(logUPnP
, msg
);
589 std::ostringstream
msgAction("Sending action ");
590 // Check for correct action name
591 ActionList::const_iterator itAction
=
592 m_SCPD
->GetActionList().find(ActionName
);
593 if (itAction
== m_SCPD
->GetActionList().end()) {
594 msg
<< "Invalid action name '" << ActionName
<<
595 "' for service '" << GetServiceType() << "'.";
596 AddDebugLogLineN(logUPnP
, msg
);
599 msgAction
<< ActionName
<< "(";
600 bool firstTime
= true;
601 // Check for correct Argument/Value pairs
602 const CUPnPAction
&action
= *(itAction
->second
);
603 for (unsigned int i
= 0; i
< ArgValue
.size(); ++i
) {
604 ArgumentList::const_iterator itArg
=
605 action
.GetArgumentList().find(ArgValue
[i
].GetArgument());
606 if (itArg
== action
.GetArgumentList().end()) {
607 msg
<< "Invalid argument name '" << ArgValue
[i
].GetArgument() <<
608 "' for action '" << action
.GetName() <<
609 "' for service '" << GetServiceType() << "'.";
610 AddDebugLogLineN(logUPnP
, msg
);
613 const CUPnPArgument
&argument
= *(itArg
->second
);
614 if (tolower(argument
.GetDirection()[0]) != 'i' ||
615 tolower(argument
.GetDirection()[1]) != 'n') {
616 msg
<< "Invalid direction for argument '" <<
617 ArgValue
[i
].GetArgument() <<
618 "' for action '" << action
.GetName() <<
619 "' for service '" << GetServiceType() << "'.";
620 AddDebugLogLineN(logUPnP
, msg
);
623 const std::string relatedStateVariableName
=
624 argument
.GetRelatedStateVariable();
625 if (!relatedStateVariableName
.empty()) {
626 ServiceStateTable::const_iterator itSVT
=
627 m_SCPD
->GetServiceStateTable().
628 find(relatedStateVariableName
);
629 if (itSVT
== m_SCPD
->GetServiceStateTable().end()) {
630 msg
<< "Inconsistent Service State Table, did not find '" <<
631 relatedStateVariableName
<<
632 "' for argument '" << argument
.GetName() <<
633 "' for action '" << action
.GetName() <<
634 "' for service '" << GetServiceType() << "'.";
635 AddDebugLogLineN(logUPnP
, msg
);
638 const CUPnPStateVariable
&stateVariable
= *(itSVT
->second
);
639 if ( !stateVariable
.GetAllowedValueList().empty() &&
640 stateVariable
.GetAllowedValueList().find(ArgValue
[i
].GetValue()) ==
641 stateVariable
.GetAllowedValueList().end()) {
642 msg
<< "Value not allowed '" << ArgValue
[i
].GetValue() <<
643 "' for state variable '" << relatedStateVariableName
<<
644 "' for argument '" << argument
.GetName() <<
645 "' for action '" << action
.GetName() <<
646 "' for service '" << GetServiceType() << "'.";
647 AddDebugLogLineN(logUPnP
, msg
);
657 ArgValue
[i
].GetArgument() <<
659 ArgValue
[i
].GetValue() <<
663 AddDebugLogLineN(logUPnP
, msgAction
);
664 // Everything is ok, make the action
665 IXML_Document
*ActionDoc
= NULL
;
666 if (ArgValue
.size()) {
667 for (unsigned int i
= 0; i
< ArgValue
.size(); ++i
) {
668 int ret
= UpnpAddToAction(
670 action
.GetName().c_str(),
671 GetServiceType().c_str(),
672 ArgValue
[i
].GetArgument().c_str(),
673 ArgValue
[i
].GetValue().c_str());
674 if (ret
!= UPNP_E_SUCCESS
) {
675 m_upnpLib
.processUPnPErrorMessage(
676 "UpnpAddToAction", ret
, NULL
, NULL
);
681 ActionDoc
= UpnpMakeAction(
682 action
.GetName().c_str(),
683 GetServiceType().c_str(),
686 msg
<< "Error: UpnpMakeAction returned NULL.";
687 AddLogLineU(false, logUPnP
, msg
);
692 // Send the action asynchronously
694 m_UPnPControlPoint
.GetUPnPClientHandle(),
695 GetAbsControlURL().c_str(),
696 GetServiceType().c_str(),
698 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
703 // Send the action synchronously
704 IXML_Document
*RespDoc
= NULL
;
705 int ret
= UpnpSendAction(
706 m_UPnPControlPoint
.GetUPnPClientHandle(),
707 GetAbsControlURL().c_str(),
708 GetServiceType().c_str(),
709 NULL
, ActionDoc
, &RespDoc
);
710 if (ret
!= UPNP_E_SUCCESS
) {
711 m_upnpLib
.processUPnPErrorMessage(
712 "UpnpSendAction", ret
, NULL
, RespDoc
);
713 ixmlDocument_free(ActionDoc
);
714 ixmlDocument_free(RespDoc
);
717 ixmlDocument_free(ActionDoc
);
719 // Check the response document
720 m_upnpLib
.ProcessActionResponse(
721 RespDoc
, action
.GetName());
723 // Free the response document
724 ixmlDocument_free(RespDoc
);
730 const std::string
CUPnPService::GetStateVariable(
731 const std::string
&stateVariableName
) const
733 std::ostringstream msg
;
735 int ret
= UpnpGetServiceVarStatus(
736 m_UPnPControlPoint
.GetUPnPClientHandle(),
737 GetAbsControlURL().c_str(),
738 stateVariableName
.c_str(),
740 if (ret
!= UPNP_E_SUCCESS
) {
741 msg
<< "GetStateVariable(\"" <<
743 "\"): in a call to UpnpGetServiceVarStatus";
744 m_upnpLib
.processUPnPErrorMessage(
745 msg
.str(), ret
, StVarVal
, NULL
);
746 return stdEmptyString
;
748 msg
<< "GetStateVariable: " <<
753 AddDebugLogLineN(logUPnP
, msg
);
754 return stdEmptyString
;
758 CUPnPDevice::CUPnPDevice(
759 const CUPnPControlPoint
&upnpControlPoint
,
761 IXML_Element
*device
,
762 const std::string
&URLBase
)
764 m_UPnPControlPoint(upnpControlPoint
),
765 m_DeviceList(upnpControlPoint
, upnpLib
, device
, URLBase
),
766 m_ServiceList(upnpControlPoint
, upnpLib
, device
, URLBase
),
767 m_deviceType (upnpLib
.Element_GetChildValueByTag(device
, "deviceType")),
768 m_friendlyName (upnpLib
.Element_GetChildValueByTag(device
, "friendlyName")),
769 m_manufacturer (upnpLib
.Element_GetChildValueByTag(device
, "manufacturer")),
770 m_manufacturerURL (upnpLib
.Element_GetChildValueByTag(device
, "manufacturerURL")),
771 m_modelDescription (upnpLib
.Element_GetChildValueByTag(device
, "modelDescription")),
772 m_modelName (upnpLib
.Element_GetChildValueByTag(device
, "modelName")),
773 m_modelNumber (upnpLib
.Element_GetChildValueByTag(device
, "modelNumber")),
774 m_modelURL (upnpLib
.Element_GetChildValueByTag(device
, "modelURL")),
775 m_serialNumber (upnpLib
.Element_GetChildValueByTag(device
, "serialNumber")),
776 m_UDN (upnpLib
.Element_GetChildValueByTag(device
, "UDN")),
777 m_UPC (upnpLib
.Element_GetChildValueByTag(device
, "UPC")),
778 m_presentationURL (upnpLib
.Element_GetChildValueByTag(device
, "presentationURL"))
780 std::ostringstream msg
;
781 int presURLlen
= strlen(URLBase
.c_str()) +
782 strlen(m_presentationURL
.c_str()) + 2;
783 std::vector
<char> vpresURL(presURLlen
);
784 char* presURL
= &vpresURL
[0];
785 int errcode
= UpnpResolveURL(
787 m_presentationURL
.c_str(),
789 if (errcode
!= UPNP_E_SUCCESS
) {
790 msg
<< "Error generating presentationURL from " <<
791 "|" << URLBase
<< "|" <<
792 m_presentationURL
<< "|.";
793 AddDebugLogLineN(logUPnP
, msg
);
795 m_presentationURL
= presURL
;
799 msg
<< "\n Device: " <<
800 "\n friendlyName: " << m_friendlyName
<<
801 "\n deviceType: " << m_deviceType
<<
802 "\n manufacturer: " << m_manufacturer
<<
803 "\n manufacturerURL: " << m_manufacturerURL
<<
804 "\n modelDescription: " << m_modelDescription
<<
805 "\n modelName: " << m_modelName
<<
806 "\n modelNumber: " << m_modelNumber
<<
807 "\n modelURL: " << m_modelURL
<<
808 "\n serialNumber: " << m_serialNumber
<<
809 "\n UDN: " << m_UDN
<<
810 "\n UPC: " << m_UPC
<<
811 "\n presentationURL: " << m_presentationURL
;
812 AddDebugLogLineN(logUPnP
, msg
);
816 CUPnPRootDevice::CUPnPRootDevice(
817 const CUPnPControlPoint
&upnpControlPoint
,
819 IXML_Element
*rootDevice
,
820 const std::string
&OriginalURLBase
,
821 const std::string
&FixedURLBase
,
822 const char *location
,
825 CUPnPDevice(upnpControlPoint
, upnpLib
, rootDevice
, FixedURLBase
),
826 m_UPnPControlPoint(upnpControlPoint
),
827 m_URLBase(OriginalURLBase
),
828 m_location(location
),
831 std::ostringstream msg
;
833 "\n Root Device: " <<
834 "\n URLBase: " << m_URLBase
<<
835 "\n Fixed URLBase: " << FixedURLBase
<<
836 "\n location: " << m_location
<<
837 "\n expires: " << m_expires
;
838 AddDebugLogLineN(logUPnP
, msg
);
842 CUPnPControlPoint
*CUPnPControlPoint::s_CtrlPoint
= NULL
;
845 CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort
)
848 m_UPnPClientHandle(),
851 m_ActivePortMappingsMap(),
852 m_RootDeviceListMutex(),
853 m_IGWDeviceDetected(false),
858 // Null string at first
859 std::ostringstream msg
;
863 char *ipAddress
= NULL
;
864 unsigned short port
= 0;
865 ret
= UpnpInit(ipAddress
, udpPort
);
866 if (ret
!= UPNP_E_SUCCESS
) {
867 msg
<< "error(UpnpInit): Error code ";
870 port
= UpnpGetServerPort();
871 ipAddress
= UpnpGetServerIpAddress();
872 msg
<< "bound to " << ipAddress
<< ":" <<
874 AddLogLineU(false, logUPnP
, msg
);
876 ret
= UpnpRegisterClient(
877 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
879 &m_UPnPClientHandle
);
880 if (ret
!= UPNP_E_SUCCESS
) {
881 msg
<< "error(UpnpRegisterClient): Error registering callback: ";
885 // We could ask for just the right device here. If the root device
886 // contains the device we want, it will respond with the full XML doc,
887 // including the root device and every sub-device it has.
889 // But lets find out what we have in our network by calling UPNP_ROOT_DEVICE.
891 // We should not search twice, because this will produce two
892 // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
894 ret
= UpnpSearchAsync(m_UPnPClientHandle
, 3, m_upnpLib
.UPNP_ROOT_DEVICE
.c_str(), NULL
);
895 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this);
896 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this);
897 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this);
898 if (ret
!= UPNP_E_SUCCESS
) {
899 msg
<< "error(UpnpSearchAsync): Error sending search request: ";
903 // Wait for the UPnP initialization to complete.
905 // Lock the search timeout mutex
906 m_WaitForSearchTimeoutMutex
.Lock();
908 // Lock it again, so that we block. Unlocking will only happen
909 // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
911 CUPnPMutexLocker
lock(m_WaitForSearchTimeoutMutex
);
918 msg
<< ret
<< ": " << m_upnpLib
.GetUPnPErrorMessage(ret
) << ".";
919 throw CUPnPException(msg
);
923 CUPnPControlPoint::~CUPnPControlPoint()
925 for( RootDeviceMap::iterator it
= m_RootDeviceMap
.begin();
926 it
!= m_RootDeviceMap
.end();
932 UpnpUnRegisterClient(m_UPnPClientHandle
);
937 bool CUPnPControlPoint::AddPortMappings(
938 std::vector
<CUPnPPortMapping
> &upnpPortMapping
)
940 std::ostringstream msg
;
941 if (!WanServiceDetected()) {
942 msg
<< "UPnP Error: "
943 "CUPnPControlPoint::AddPortMapping: "
944 "WAN Service not detected.";
945 AddLogLineU(true, logUPnP
, msg
);
949 int n
= upnpPortMapping
.size();
952 // Check the number of port mappings before
953 std::istringstream
PortMappingNumberOfEntries(
954 m_WanService
->GetStateVariable(
955 "PortMappingNumberOfEntries"));
956 unsigned long oldNumberOfEntries
;
957 PortMappingNumberOfEntries
>> oldNumberOfEntries
;
959 // Add the enabled port mappings
960 for (int i
= 0; i
< n
; ++i
) {
961 if (upnpPortMapping
[i
].getEnabled() == "1") {
962 // Add the mapping to the control point
963 // active mappings list
964 m_ActivePortMappingsMap
[upnpPortMapping
[i
].getKey()] =
967 // Add the port mapping
968 PrivateAddPortMapping(upnpPortMapping
[i
]);
972 // Test some variables, this is deprecated, might not work
974 m_WanService
->GetStateVariable("ConnectionType");
975 m_WanService
->GetStateVariable("PossibleConnectionTypes");
976 m_WanService
->GetStateVariable("ConnectionStatus");
977 m_WanService
->GetStateVariable("Uptime");
978 m_WanService
->GetStateVariable("LastConnectionError");
979 m_WanService
->GetStateVariable("RSIPAvailable");
980 m_WanService
->GetStateVariable("NATEnabled");
981 m_WanService
->GetStateVariable("ExternalIPAddress");
982 m_WanService
->GetStateVariable("PortMappingNumberOfEntries");
983 m_WanService
->GetStateVariable("PortMappingLeaseDuration");
986 std::vector
<CUPnPArgumentValue
> argval
;
988 m_WanService
->Execute("GetStatusInfo", argval
);
991 // These do not work. Their value must be requested for a
992 // specific port mapping.
993 m_WanService
->GetStateVariable("PortMappingEnabled");
994 m_WanService
->GetStateVariable("RemoteHost");
995 m_WanService
->GetStateVariable("ExternalPort");
996 m_WanService
->GetStateVariable("InternalPort");
997 m_WanService
->GetStateVariable("PortMappingProtocol");
998 m_WanService
->GetStateVariable("InternalClient");
999 m_WanService
->GetStateVariable("PortMappingDescription");
1004 msg
<< "CUPnPControlPoint::DeletePortMappings: "
1005 "m_ActivePortMappingsMap.size() == " <<
1006 m_ActivePortMappingsMap
.size();
1007 AddDebugLogLineN(logUPnP
, msg
);
1009 // Not very good, must find a better test
1010 PortMappingNumberOfEntries
.str(
1011 m_WanService
->GetStateVariable(
1012 "PortMappingNumberOfEntries"));
1013 unsigned long newNumberOfEntries
;
1014 PortMappingNumberOfEntries
>> newNumberOfEntries
;
1015 ok
= newNumberOfEntries
- oldNumberOfEntries
== 4;
1021 void CUPnPControlPoint::RefreshPortMappings()
1023 for ( PortMappingMap::iterator it
= m_ActivePortMappingsMap
.begin();
1024 it
!= m_ActivePortMappingsMap
.end();
1026 PrivateAddPortMapping(it
->second
);
1030 m_WanService
->GetStateVariable("PortMappingNumberOfEntries");
1034 bool CUPnPControlPoint::PrivateAddPortMapping(
1035 CUPnPPortMapping
&upnpPortMapping
)
1037 // Get an IP address. The UPnP server one must do.
1038 std::string
ipAddress(UpnpGetServerIpAddress());
1040 // Start building the action
1041 std::string
actionName("AddPortMapping");
1042 std::vector
<CUPnPArgumentValue
> argval(8);
1044 // Action parameters
1045 argval
[0].SetArgument("NewRemoteHost");
1046 argval
[0].SetValue("");
1047 argval
[1].SetArgument("NewExternalPort");
1048 argval
[1].SetValue(upnpPortMapping
.getPort());
1049 argval
[2].SetArgument("NewProtocol");
1050 argval
[2].SetValue(upnpPortMapping
.getProtocol());
1051 argval
[3].SetArgument("NewInternalPort");
1052 argval
[3].SetValue(upnpPortMapping
.getPort());
1053 argval
[4].SetArgument("NewInternalClient");
1054 argval
[4].SetValue(ipAddress
);
1055 argval
[5].SetArgument("NewEnabled");
1056 argval
[5].SetValue("1");
1057 argval
[6].SetArgument("NewPortMappingDescription");
1058 argval
[6].SetValue(upnpPortMapping
.getDescription());
1059 argval
[7].SetArgument("NewLeaseDuration");
1060 argval
[7].SetValue("0");
1064 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1065 it
!= m_ServiceMap
.end(); ++it
) {
1066 ret
&= it
->second
->Execute(actionName
, argval
);
1073 bool CUPnPControlPoint::DeletePortMappings(
1074 std::vector
<CUPnPPortMapping
> &upnpPortMapping
)
1076 std::ostringstream msg
;
1077 if (!WanServiceDetected()) {
1078 msg
<< "UPnP Error: "
1079 "CUPnPControlPoint::DeletePortMapping: "
1080 "WAN Service not detected.";
1081 AddLogLineU(true, logUPnP
, msg
);
1085 int n
= upnpPortMapping
.size();
1088 // Check the number of port mappings before
1089 std::istringstream
PortMappingNumberOfEntries(
1090 m_WanService
->GetStateVariable(
1091 "PortMappingNumberOfEntries"));
1092 unsigned long oldNumberOfEntries
;
1093 PortMappingNumberOfEntries
>> oldNumberOfEntries
;
1095 // Delete the enabled port mappings
1096 for (int i
= 0; i
< n
; ++i
) {
1097 if (upnpPortMapping
[i
].getEnabled() == "1") {
1098 // Delete the mapping from the control point
1099 // active mappings list
1100 PortMappingMap::iterator it
=
1101 m_ActivePortMappingsMap
.find(
1102 upnpPortMapping
[i
].getKey());
1103 if (it
!= m_ActivePortMappingsMap
.end()) {
1104 m_ActivePortMappingsMap
.erase(it
);
1106 msg
<< "UPnP Error: "
1107 "CUPnPControlPoint::DeletePortMapping: "
1108 "Mapping was not found in the active "
1110 AddLogLineU(true, logUPnP
, msg
);
1113 // Delete the port mapping
1114 PrivateDeletePortMapping(upnpPortMapping
[i
]);
1120 msg
<< "CUPnPControlPoint::DeletePortMappings: "
1121 "m_ActivePortMappingsMap.size() == " <<
1122 m_ActivePortMappingsMap
.size();
1123 AddDebugLogLineN(logUPnP
, msg
);
1125 // Not very good, must find a better test
1126 PortMappingNumberOfEntries
.str(
1127 m_WanService
->GetStateVariable(
1128 "PortMappingNumberOfEntries"));
1129 unsigned long newNumberOfEntries
;
1130 PortMappingNumberOfEntries
>> newNumberOfEntries
;
1131 ok
= oldNumberOfEntries
- newNumberOfEntries
== 4;
1137 bool CUPnPControlPoint::PrivateDeletePortMapping(
1138 CUPnPPortMapping
&upnpPortMapping
)
1140 // Start building the action
1141 std::string
actionName("DeletePortMapping");
1142 std::vector
<CUPnPArgumentValue
> argval(3);
1144 // Action parameters
1145 argval
[0].SetArgument("NewRemoteHost");
1146 argval
[0].SetValue("");
1147 argval
[1].SetArgument("NewExternalPort");
1148 argval
[1].SetValue(upnpPortMapping
.getPort());
1149 argval
[2].SetArgument("NewProtocol");
1150 argval
[2].SetValue(upnpPortMapping
.getProtocol());
1154 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1155 it
!= m_ServiceMap
.end(); ++it
) {
1156 ret
&= it
->second
->Execute(actionName
, argval
);
1163 // This function is static
1164 int CUPnPControlPoint::Callback(Upnp_EventType EventType
, void *Event
, void * /*Cookie*/)
1166 std::ostringstream msg
;
1167 std::ostringstream msg2
;
1168 // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
1169 // happen with a wrong cookie and... boom!
1170 // CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
1171 CUPnPControlPoint
*upnpCP
= CUPnPControlPoint::s_CtrlPoint
;
1173 //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
1174 switch (EventType
) {
1175 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
:
1176 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
1177 msg
<< "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): ";
1178 msg2
<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
1180 case UPNP_DISCOVERY_SEARCH_RESULT
: {
1181 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n");
1182 msg
<< "error(UPNP_DISCOVERY_SEARCH_RESULT): ";
1183 msg2
<< "UPNP_DISCOVERY_SEARCH_RESULT: ";
1186 struct Upnp_Discovery
*d_event
= (struct Upnp_Discovery
*)Event
;
1187 IXML_Document
*doc
= NULL
;
1189 if (d_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1190 msg
<< upnpCP
->m_upnpLib
.GetUPnPErrorMessage(d_event
->ErrCode
) << ".";
1191 AddDebugLogLineC(logUPnP
, msg
);
1193 // Get the XML tree device description in doc
1194 ret
= UpnpDownloadXmlDoc(d_event
->Location
, &doc
);
1195 if (ret
!= UPNP_E_SUCCESS
) {
1196 msg
<< "Error retrieving device description from " <<
1197 d_event
->Location
<< ": " <<
1198 upnpCP
->m_upnpLib
.GetUPnPErrorMessage(ret
) <<
1200 AddDebugLogLineC(logUPnP
, msg
);
1202 msg2
<< "Retrieving device description from " <<
1203 d_event
->Location
<< ".";
1204 AddDebugLogLineN(logUPnP
, msg2
);
1207 // Get the root node
1208 IXML_Element
*root
=
1209 upnpCP
->m_upnpLib
.Element_GetRootElement(doc
);
1210 // Extract the URLBase
1211 const std::string urlBase
= upnpCP
->m_upnpLib
.
1212 Element_GetChildValueByTag(root
, "URLBase");
1213 // Get the root device
1214 IXML_Element
*rootDevice
= upnpCP
->m_upnpLib
.
1215 Element_GetFirstChildByTag(root
, "device");
1216 // Extract the deviceType
1217 std::string
devType(upnpCP
->m_upnpLib
.
1218 Element_GetChildValueByTag(rootDevice
, "deviceType"));
1219 // Only add device if it is an InternetGatewayDevice
1220 if (stdStringIsEqualCI(devType
, upnpCP
->m_upnpLib
.UPNP_DEVICE_IGW
)) {
1221 // This condition can be used to auto-detect
1222 // the UPnP device we are interested in.
1223 // Obs.: Don't block the entry here on this
1224 // condition! There may be more than one device,
1225 // and the first that enters may not be the one
1226 // we are interested in!
1227 upnpCP
->SetIGWDeviceDetected(true);
1228 // Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
1229 // we don't want to spam our logs.
1230 if (EventType
!= UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
) {
1231 msg
.str("Internet Gateway Device Detected.");
1232 AddLogLineU(true, logUPnP
, msg
);
1234 // Add the root device to our list
1235 upnpCP
->AddRootDevice(rootDevice
, urlBase
,
1236 d_event
->Location
, d_event
->Expires
);
1238 // Free the XML doc tree
1239 ixmlDocument_free(doc
);
1243 case UPNP_DISCOVERY_SEARCH_TIMEOUT
: {
1244 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1246 msg
<< "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1247 AddDebugLogLineN(logUPnP
, msg
);
1249 // Unlock the search timeout mutex
1250 upnpCP
->m_WaitForSearchTimeoutMutex
.Unlock();
1254 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE
: {
1255 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
1256 // UPnP Device Removed
1257 struct Upnp_Discovery
*dab_event
= (struct Upnp_Discovery
*)Event
;
1258 if (dab_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1259 msg
<< "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
1260 upnpCP
->m_upnpLib
.GetUPnPErrorMessage(dab_event
->ErrCode
) <<
1262 AddDebugLogLineC(logUPnP
, msg
);
1264 std::string devType
= dab_event
->DeviceType
;
1265 // Check for an InternetGatewayDevice and removes it from the list
1266 std::transform(devType
.begin(), devType
.end(), devType
.begin(), tolower
);
1267 if (stdStringIsEqualCI(devType
, upnpCP
->m_upnpLib
.UPNP_DEVICE_IGW
)) {
1268 upnpCP
->RemoveRootDevice(dab_event
->DeviceId
);
1272 case UPNP_EVENT_RECEIVED
: {
1273 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1275 struct Upnp_Event
*e_event
= (struct Upnp_Event
*)Event
;
1276 const std::string Sid
= e_event
->Sid
;
1278 upnpCP
->OnEventReceived(Sid
, e_event
->EventKey
, e_event
->ChangedVariables
);
1281 case UPNP_EVENT_SUBSCRIBE_COMPLETE
:
1282 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
1283 msg
<< "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): ";
1284 goto upnpEventRenewalComplete
;
1285 case UPNP_EVENT_UNSUBSCRIBE_COMPLETE
:
1286 //fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
1287 msg
<< "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): ";
1288 goto upnpEventRenewalComplete
;
1289 case UPNP_EVENT_RENEWAL_COMPLETE
: {
1290 //fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n");
1291 msg
<< "error(UPNP_EVENT_RENEWAL_COMPLETE): ";
1292 upnpEventRenewalComplete
:
1293 struct Upnp_Event_Subscribe
*es_event
=
1294 (struct Upnp_Event_Subscribe
*)Event
;
1295 if (es_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1296 msg
<< "Error in Event Subscribe Callback";
1297 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1298 msg
.str(), es_event
->ErrCode
, NULL
, NULL
);
1301 TvCtrlPointHandleSubscribeUpdate(
1302 es_event
->PublisherUrl
,
1304 es_event
->TimeOut
);
1311 case UPNP_EVENT_AUTORENEWAL_FAILED
:
1312 //fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n");
1313 msg
<< "error(UPNP_EVENT_AUTORENEWAL_FAILED): ";
1314 msg2
<< "UPNP_EVENT_AUTORENEWAL_FAILED: ";
1315 goto upnpEventSubscriptionExpired
;
1316 case UPNP_EVENT_SUBSCRIPTION_EXPIRED
: {
1317 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
1318 msg
<< "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): ";
1319 msg2
<< "UPNP_EVENT_SUBSCRIPTION_EXPIRED: ";
1320 upnpEventSubscriptionExpired
:
1321 struct Upnp_Event_Subscribe
*es_event
=
1322 (struct Upnp_Event_Subscribe
*)Event
;
1325 int ret
= UpnpSubscribe(
1326 upnpCP
->m_UPnPClientHandle
,
1327 es_event
->PublisherUrl
,
1330 if (ret
!= UPNP_E_SUCCESS
) {
1331 msg
<< "Error Subscribing to EventURL";
1332 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1333 msg
.str(), es_event
->ErrCode
, NULL
, NULL
);
1335 ServiceMap::iterator it
=
1336 upnpCP
->m_ServiceMap
.find(es_event
->PublisherUrl
);
1337 if (it
!= upnpCP
->m_ServiceMap
.end()) {
1338 CUPnPService
&service
= *(it
->second
);
1339 service
.SetTimeout(TimeOut
);
1340 service
.SetSID(newSID
);
1341 msg2
<< "Re-subscribed to EventURL '" <<
1342 es_event
->PublisherUrl
<<
1343 "' with SID == '" <<
1345 AddDebugLogLineC(logUPnP
, msg2
);
1346 // In principle, we should test to see if the
1347 // service is the same. But here we only have one
1349 upnpCP
->RefreshPortMappings();
1351 msg
<< "Error: did not find service " <<
1352 newSID
<< " in the service map.";
1353 AddDebugLogLineC(logUPnP
, msg
);
1358 case UPNP_CONTROL_ACTION_COMPLETE
: {
1359 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n");
1360 // This is here if we choose to do this asynchronously
1361 struct Upnp_Action_Complete
*a_event
=
1362 (struct Upnp_Action_Complete
*)Event
;
1363 if (a_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1364 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1365 "UpnpSendActionAsync",
1366 a_event
->ErrCode
, NULL
,
1367 a_event
->ActionResult
);
1369 // Check the response document
1370 upnpCP
->m_upnpLib
.ProcessActionResponse(
1371 a_event
->ActionResult
,
1372 "<UpnpSendActionAsync>");
1374 /* No need for any processing here, just print out results.
1375 * Service state table updates are handled by events.
1379 case UPNP_CONTROL_GET_VAR_COMPLETE
: {
1380 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n");
1381 msg
<< "error(UPNP_CONTROL_GET_VAR_COMPLETE): ";
1382 struct Upnp_State_Var_Complete
*sv_event
=
1383 (struct Upnp_State_Var_Complete
*)Event
;
1384 if (sv_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1385 msg
<< "m_UpnpGetServiceVarStatusAsync";
1386 upnpCP
->m_upnpLib
.processUPnPErrorMessage(
1387 msg
.str(), sv_event
->ErrCode
, NULL
, NULL
);
1390 // Warning: The use of UpnpGetServiceVarStatus and
1391 // UpnpGetServiceVarStatusAsync is deprecated by the
1393 TvCtrlPointHandleGetVar(
1395 sv_event
->StateVarName
,
1396 sv_event
->CurrentVal
);
1401 // ignore these cases, since this is not a device
1402 case UPNP_CONTROL_GET_VAR_REQUEST
:
1403 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n");
1404 msg
<< "error(UPNP_CONTROL_GET_VAR_REQUEST): ";
1405 goto eventSubscriptionRequest
;
1406 case UPNP_CONTROL_ACTION_REQUEST
:
1407 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n");
1408 msg
<< "error(UPNP_CONTROL_ACTION_REQUEST): ";
1409 goto eventSubscriptionRequest
;
1410 case UPNP_EVENT_SUBSCRIPTION_REQUEST
:
1411 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
1412 msg
<< "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): ";
1413 eventSubscriptionRequest
:
1414 msg
<< "This is not a UPnP Device, this is a UPnP Control Point, event ignored.";
1415 AddDebugLogLineC(logUPnP
, msg
);
1418 // Humm, this is not good, we forgot to handle something...
1420 "Callback: default... Unknown event:'%d', not good.\n",
1422 msg
<< "error(UPnP::Callback): Event not handled:'" <<
1424 fprintf(stderr
, "%s\n", msg
.str().c_str());
1425 AddDebugLogLineC(logUPnP
, msg
);
1426 // Better not throw in the callback. Who would catch it?
1427 //throw CUPnPException(msg);
1435 void CUPnPControlPoint::OnEventReceived(
1436 const std::string
&Sid
,
1438 IXML_Document
*ChangedVariablesDoc
)
1440 std::ostringstream msg
;
1441 msg
<< "UPNP_EVENT_RECEIVED:" <<
1442 "\n SID: " << Sid
<<
1443 "\n Key: " << EventKey
<<
1444 "\n Property list:";
1445 IXML_Element
*root
=
1446 m_upnpLib
.Element_GetRootElement(ChangedVariablesDoc
);
1447 IXML_Element
*child
=
1448 m_upnpLib
.Element_GetFirstChild(root
);
1451 IXML_Element
*child2
=
1452 m_upnpLib
.Element_GetFirstChild(child
);
1453 const DOMString childTag
=
1454 m_upnpLib
.Element_GetTag(child2
);
1455 std::string childValue
=
1456 m_upnpLib
.Element_GetTextValue(child2
);
1460 child
= m_upnpLib
.Element_GetNextSibling(child
);
1463 msg
<< "\n Empty property list.";
1465 AddDebugLogLineC(logUPnP
, msg
);
1466 // Freeing that doc segfaults. Probably should not be freed.
1467 //ixmlDocument_free(ChangedVariablesDoc);
1471 void CUPnPControlPoint::AddRootDevice(
1472 IXML_Element
*rootDevice
, const std::string
&urlBase
,
1473 const char *location
, int expires
)
1475 // Lock the Root Device List
1476 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1478 // Root node's URLBase
1479 std::string
OriginalURLBase(urlBase
);
1480 std::string
FixedURLBase(OriginalURLBase
.empty() ?
1484 // Get the UDN (Unique Device Name)
1486 m_upnpLib
.Element_GetChildValueByTag(rootDevice
, "UDN"));
1487 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1488 bool alreadyAdded
= it
!= m_RootDeviceMap
.end();
1490 // Just set the expires field
1491 it
->second
->SetExpires(expires
);
1493 // Add a new root device to the root device list
1494 CUPnPRootDevice
*upnpRootDevice
= new CUPnPRootDevice(
1495 *this, m_upnpLib
, rootDevice
,
1496 OriginalURLBase
, FixedURLBase
,
1498 m_RootDeviceMap
[upnpRootDevice
->GetUDN()] = upnpRootDevice
;
1503 void CUPnPControlPoint::RemoveRootDevice(const char *udn
)
1505 // Lock the Root Device List
1506 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1509 std::string
UDN(udn
);
1510 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1511 if (it
!= m_RootDeviceMap
.end()) {
1513 m_RootDeviceMap
.erase(UDN
);
1518 void CUPnPControlPoint::Subscribe(CUPnPService
&service
)
1520 std::ostringstream msg
;
1522 IXML_Document
*scpdDoc
= NULL
;
1523 int errcode
= UpnpDownloadXmlDoc(
1524 service
.GetAbsSCPDURL().c_str(), &scpdDoc
);
1525 if (errcode
== UPNP_E_SUCCESS
) {
1526 // Get the root node of this service (the SCPD Document)
1527 IXML_Element
*scpdRoot
=
1528 m_upnpLib
.Element_GetRootElement(scpdDoc
);
1529 CUPnPSCPD
*scpd
= new CUPnPSCPD(*this, m_upnpLib
,
1530 scpdRoot
, service
.GetAbsSCPDURL());
1531 service
.SetSCPD(scpd
);
1532 m_ServiceMap
[service
.GetAbsEventSubURL()] = &service
;
1533 msg
<< "Successfully retrieved SCPD Document for service " <<
1534 service
.GetServiceType() << ", absEventSubURL: " <<
1535 service
.GetAbsEventSubURL() << ".";
1536 AddLogLineU(true, logUPnP
, msg
);
1539 // Now try to subscribe to this service. If the subscription
1540 // is not successfull, we will not be notified about events,
1541 // but it may be possible to use the service anyway.
1542 errcode
= UpnpSubscribe(m_UPnPClientHandle
,
1543 service
.GetAbsEventSubURL().c_str(),
1544 service
.GetTimeoutAddr(),
1546 if (errcode
== UPNP_E_SUCCESS
) {
1547 msg
<< "Successfully subscribed to service " <<
1548 service
.GetServiceType() << ", absEventSubURL: " <<
1549 service
.GetAbsEventSubURL() << ".";
1550 AddLogLineU(true, logUPnP
, msg
);
1552 msg
<< "Error subscribing to service " <<
1553 service
.GetServiceType() << ", absEventSubURL: " <<
1554 service
.GetAbsEventSubURL() << ", error: " <<
1555 m_upnpLib
.GetUPnPErrorMessage(errcode
) << ".";
1559 msg
<< "Error getting SCPD Document from " <<
1560 service
.GetAbsSCPDURL() << ".";
1561 AddLogLineU(true, logUPnP
, msg
);
1568 AddLogLineU(true, logUPnP
, msg
);
1572 void CUPnPControlPoint::Unsubscribe(CUPnPService
&service
)
1574 ServiceMap::iterator it
= m_ServiceMap
.find(service
.GetAbsEventSubURL());
1575 if (it
!= m_ServiceMap
.end()) {
1576 m_ServiceMap
.erase(it
);
1577 UpnpUnSubscribe(m_UPnPClientHandle
, service
.GetSID());
1581 #endif /* ENABLE_UPNP */