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
26 #include "config.h" // Needed for ENABLE_UPNP
30 // check for broken Debian-hacked libUPnP
32 #ifdef STRING_H // defined in UpnpString.h Yes, I would have liked UPNPSTRING_H much better.
33 #define BROKEN_DEBIAN_LIBUPNP
38 #include <algorithm> // For transform()
40 #ifdef BROKEN_DEBIAN_LIBUPNP
41 #define GET_UPNP_STRING(a) UpnpString_get_String(a)
43 #define GET_UPNP_STRING(a) (a)
46 std::string stdEmptyString
;
48 const char s_argument
[] = "argument";
49 const char s_argumentList
[] = "argumentList";
50 const char s_action
[] = "action";
51 const char s_actionList
[] = "actionList";
52 const char s_allowedValue
[] = "allowedValue";
53 const char s_allowedValueList
[] = "allowedValueList";
54 const char s_stateVariable
[] = "stateVariable";
55 const char s_serviceStateTable
[] = "serviceStateTable";
56 const char s_service
[] = "service";
57 const char s_serviceList
[] = "serviceList";
58 const char s_device
[] = "device";
59 const char s_deviceList
[] = "deviceList";
62 * Case insensitive std::string comparison
64 static bool stdStringIsEqualCI(const std::string
&s1
, const std::string
&s2
)
68 std::transform(ns1
.begin(), ns1
.end(), ns1
.begin(), tolower
);
69 std::transform(ns2
.begin(), ns2
.end(), ns2
.begin(), tolower
);
74 CUPnPPortMapping::CUPnPPortMapping(
76 const std::string
&protocol
,
78 const std::string
&description
)
82 m_enabled(enabled
? "1" : "0"),
83 m_description(description
),
86 std::ostringstream oss
;
89 m_key
= m_protocol
+ m_port
;
94 static const std::string
ROOT_DEVICE("upnp:rootdevice");
97 static const std::string
IGW("urn:schemas-upnp-org:device:InternetGatewayDevice:1");
98 static const std::string
WAN("urn:schemas-upnp-org:device:WANDevice:1");
99 static const std::string
WAN_Connection("urn:schemas-upnp-org:device:WANConnectionDevice:1");
100 static const std::string
LAN("urn:schemas-upnp-org:device:LANDevice:1");
104 static const std::string
Layer3_Forwarding("urn:schemas-upnp-org:service:Layer3Forwarding:1");
105 static const std::string
WAN_Common_Interface_Config("urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1");
106 static const std::string
WAN_IP_Connection("urn:schemas-upnp-org:service:WANIPConnection:1");
107 static const std::string
WAN_PPP_Connection("urn:schemas-upnp-org:service:WANPPPConnection:1");
110 static std::string
ProcessErrorMessage(
111 const std::string
&messsage
,
113 const DOMString errorString
,
116 std::ostringstream msg
;
117 if (errorString
== NULL
|| *errorString
== 0) {
118 errorString
= "Not available";
126 msg
<< e
.getErrorCode() <<
127 "', Error description :'" <<
128 e
.getErrorDescription() <<
132 "', Error description :'" <<
136 AddDebugLogLineN(logUPnP
, msg
);
140 ": UPnP SDK error: " <<
141 UpnpGetErrorMessage(errorCode
) <<
142 " (" << errorCode
<< ").";
143 AddDebugLogLineN(logUPnP
, msg
);
150 static void ProcessActionResponse(
151 IXML_Document
*RespDoc
,
152 const std::string
&actionName
)
154 std::ostringstream msg
;
156 IXML_Element
*root
= IXML::Document::GetRootElement(RespDoc
);
157 IXML_Element
*child
= IXML::Element::GetFirstChild(root
);
160 const DOMString childTag
= IXML::Element::GetTag(child
);
161 std::string childValue
= IXML::Element::GetTextValue(child
);
165 child
= IXML::Element::GetNextSibling(child
);
168 msg
<< "\n Empty response for action '" <<
171 AddDebugLogLineN(logUPnP
, msg
);
174 } /* namespace UPnP */
180 * \brief Returns the root node of a given document.
182 IXML_Element
*Document::GetRootElement(IXML_Document
*doc
)
184 return reinterpret_cast<IXML_Element
*>(ixmlNode_getFirstChild(&doc
->n
));
188 * \brief Frees the given document.
190 * \note Any nodes extracted via any other interface function will become
191 * invalid after this call unless explicitly cloned.
193 inline void Document::Free(IXML_Document
*doc
)
195 ixmlDocument_free(doc
);
201 * \brief Returns the first child of a given element.
203 IXML_Element
*GetFirstChild(IXML_Element
*parent
)
205 return reinterpret_cast<IXML_Element
*>(ixmlNode_getFirstChild(&parent
->n
));
211 * \brief Returns the next sibling of a given child.
213 IXML_Element
*GetNextSibling(IXML_Element
*child
)
215 return reinterpret_cast<IXML_Element
*>(ixmlNode_getNextSibling(&child
->n
));
220 * \brief Returns the element tag (name)
222 const DOMString
GetTag(IXML_Element
*element
)
224 return ixmlNode_getNodeName(&element
->n
);
229 * \brief Returns the TEXT node value of the current node.
231 const std::string
GetTextValue(IXML_Element
*element
)
234 return stdEmptyString
;
236 IXML_Node
*text
= ixmlNode_getFirstChild(&element
->n
);
237 const DOMString s
= ixmlNode_getNodeValue(text
);
248 * \brief Returns the TEXT node value of the first child matching tag.
250 const std::string
GetChildValueByTag(IXML_Element
*element
, const DOMString tag
)
252 return GetTextValue(GetFirstChildByTag(element
, tag
));
257 * \brief Returns the first child element that matches the requested tag or
260 IXML_Element
*GetFirstChildByTag(IXML_Element
*element
, const DOMString tag
)
262 if (!element
|| !tag
) {
266 IXML_Node
*child
= ixmlNode_getFirstChild(&element
->n
);
267 const DOMString childTag
= ixmlNode_getNodeName(child
);
268 while(child
&& childTag
&& strcmp(tag
, childTag
)) {
269 child
= ixmlNode_getNextSibling(child
);
270 childTag
= ixmlNode_getNodeName(child
);
273 return reinterpret_cast<IXML_Element
*>(child
);
278 * \brief Returns the next sibling element that matches the requested tag. Should be
279 * used with the return value of GetFirstChildByTag().
281 IXML_Element
*GetNextSiblingByTag(IXML_Element
*element
, const DOMString tag
)
283 if (!element
|| !tag
) {
287 IXML_Node
*child
= &element
->n
;
288 const DOMString childTag
= NULL
;
290 child
= ixmlNode_getNextSibling(child
);
291 childTag
= ixmlNode_getNodeName(child
);
292 } while(child
&& childTag
&& strcmp(tag
, childTag
));
294 return reinterpret_cast<IXML_Element
*>(child
);
298 const std::string
GetAttributeByTag(IXML_Element
*element
, const DOMString tag
)
300 IXML_NamedNodeMap
*NamedNodeMap
= ixmlNode_getAttributes(&element
->n
);
301 IXML_Node
*attribute
= ixmlNamedNodeMap_getNamedItem(NamedNodeMap
, tag
);
302 const DOMString s
= ixmlNode_getNodeValue(attribute
);
307 ixmlNamedNodeMap_free(NamedNodeMap
);
312 } /* namespace Element */
314 } /* namespace IXML */
317 CUPnPError::CUPnPError(IXML_Document
*errorDoc
)
319 m_root (IXML::Document::GetRootElement(errorDoc
)),
320 m_ErrorCode (IXML::Element::GetChildValueByTag(m_root
, "errorCode")),
321 m_ErrorDescription(IXML::Element::GetChildValueByTag(m_root
, "errorDescription"))
326 CUPnPArgument::CUPnPArgument(
327 const CUPnPControlPoint
&WXUNUSED(upnpControlPoint
),
328 IXML_Element
*argument
,
329 const std::string
&WXUNUSED(SCPDURL
))
331 m_name (IXML::Element::GetChildValueByTag(argument
, "name")),
332 m_direction (IXML::Element::GetChildValueByTag(argument
, "direction")),
333 m_retval (IXML::Element::GetFirstChildByTag(argument
, "retval")),
334 m_relatedStateVariable(IXML::Element::GetChildValueByTag(argument
, "relatedStateVariable"))
336 std::ostringstream msg
;
337 msg
<< "\n Argument:" <<
338 "\n name: " << m_name
<<
339 "\n direction: " << m_direction
<<
340 "\n retval: " << m_retval
<<
341 "\n relatedStateVariable: " << m_relatedStateVariable
;
342 AddDebugLogLineN(logUPnP
, msg
);
346 CUPnPAction::CUPnPAction(
347 const CUPnPControlPoint
&upnpControlPoint
,
348 IXML_Element
*action
,
349 const std::string
&SCPDURL
)
351 m_ArgumentList(upnpControlPoint
, action
, SCPDURL
),
352 m_name(IXML::Element::GetChildValueByTag(action
, "name"))
354 std::ostringstream msg
;
355 msg
<< "\n Action:" <<
356 "\n name: " << m_name
;
357 AddDebugLogLineN(logUPnP
, msg
);
361 CUPnPAllowedValue::CUPnPAllowedValue(
362 const CUPnPControlPoint
&WXUNUSED(upnpControlPoint
),
363 IXML_Element
*allowedValue
,
364 const std::string
&WXUNUSED(SCPDURL
))
366 m_allowedValue(IXML::Element::GetTextValue(allowedValue
))
368 std::ostringstream msg
;
369 msg
<< "\n AllowedValue:" <<
370 "\n allowedValue: " << m_allowedValue
;
371 AddDebugLogLineN(logUPnP
, msg
);
375 CUPnPStateVariable::CUPnPStateVariable(
376 const CUPnPControlPoint
&upnpControlPoint
,
377 IXML_Element
*stateVariable
,
378 const std::string
&SCPDURL
)
380 m_AllowedValueList(upnpControlPoint
, stateVariable
, SCPDURL
),
381 m_name (IXML::Element::GetChildValueByTag(stateVariable
, "name")),
382 m_dataType (IXML::Element::GetChildValueByTag(stateVariable
, "dataType")),
383 m_defaultValue(IXML::Element::GetChildValueByTag(stateVariable
, "defaultValue")),
384 m_sendEvents (IXML::Element::GetAttributeByTag (stateVariable
, "sendEvents"))
386 std::ostringstream msg
;
387 msg
<< "\n StateVariable:" <<
388 "\n name: " << m_name
<<
389 "\n dataType: " << m_dataType
<<
390 "\n defaultValue: " << m_defaultValue
<<
391 "\n sendEvents: " << m_sendEvents
;
392 AddDebugLogLineN(logUPnP
, msg
);
396 CUPnPSCPD::CUPnPSCPD(
397 const CUPnPControlPoint
&upnpControlPoint
,
399 const std::string
&SCPDURL
)
401 m_ActionList(upnpControlPoint
, scpd
, SCPDURL
),
402 m_ServiceStateTable(upnpControlPoint
, scpd
, SCPDURL
),
408 CUPnPArgumentValue::CUPnPArgumentValue()
416 CUPnPArgumentValue::CUPnPArgumentValue(
417 const std::string
&argument
, const std::string
&value
)
419 m_argument(argument
),
425 CUPnPService::CUPnPService(
426 const CUPnPControlPoint
&upnpControlPoint
,
427 IXML_Element
*service
,
428 const std::string
&URLBase
)
430 m_UPnPControlPoint(upnpControlPoint
),
431 m_serviceType(IXML::Element::GetChildValueByTag(service
, "serviceType")),
432 m_serviceId (IXML::Element::GetChildValueByTag(service
, "serviceId")),
433 m_SCPDURL (IXML::Element::GetChildValueByTag(service
, "SCPDURL")),
434 m_controlURL (IXML::Element::GetChildValueByTag(service
, "controlURL")),
435 m_eventSubURL(IXML::Element::GetChildValueByTag(service
, "eventSubURL")),
439 std::ostringstream msg
;
442 memset(m_SID
, 0 , sizeof(Upnp_SID
));
444 std::vector
<char> vscpdURL(URLBase
.length() + m_SCPDURL
.length() + 1);
445 char *scpdURL
= &vscpdURL
[0];
446 errcode
= UpnpResolveURL(
450 if( errcode
!= UPNP_E_SUCCESS
) {
451 msg
<< "Error generating scpdURL from " <<
452 "|" << URLBase
<< "|" <<
454 AddDebugLogLineN(logUPnP
, msg
);
456 m_absSCPDURL
= scpdURL
;
459 std::vector
<char> vcontrolURL(
460 URLBase
.length() + m_controlURL
.length() + 1);
461 char *controlURL
= &vcontrolURL
[0];
462 errcode
= UpnpResolveURL(
464 m_controlURL
.c_str(),
466 if( errcode
!= UPNP_E_SUCCESS
) {
467 msg
<< "Error generating controlURL from " <<
468 "|" << URLBase
<< "|" <<
469 m_controlURL
<< "|.";
470 AddDebugLogLineN(logUPnP
, msg
);
472 m_absControlURL
= controlURL
;
475 std::vector
<char> veventURL(
476 URLBase
.length() + m_eventSubURL
.length() + 1);
477 char *eventURL
= &veventURL
[0];
478 errcode
= UpnpResolveURL(
480 m_eventSubURL
.c_str(),
482 if( errcode
!= UPNP_E_SUCCESS
) {
483 msg
<< "Error generating eventURL from " <<
484 "|" << URLBase
<< "|" <<
485 m_eventSubURL
<< "|.";
486 AddDebugLogLineN(logUPnP
, msg
);
488 m_absEventSubURL
= eventURL
;
491 msg
<< "\n Service:" <<
492 "\n serviceType: " << m_serviceType
<<
493 "\n serviceId: " << m_serviceId
<<
494 "\n SCPDURL: " << m_SCPDURL
<<
495 "\n absSCPDURL: " << m_absSCPDURL
<<
496 "\n controlURL: " << m_controlURL
<<
497 "\n absControlURL: " << m_absControlURL
<<
498 "\n eventSubURL: " << m_eventSubURL
<<
499 "\n absEventSubURL: " << m_absEventSubURL
;
500 AddDebugLogLineN(logUPnP
, msg
);
502 if (m_serviceType
== UPnP::Service::WAN_IP_Connection
||
503 m_serviceType
== UPnP::Service::WAN_PPP_Connection
) {
505 m_serviceType
== UPnP::Service::WAN_PPP_Connection
||
506 m_serviceType
== UPnP::Service::WAN_Common_Interface_Config
||
507 m_serviceType
== UPnP::Service::Layer3_Forwarding
) {
510 //#warning Delete this code on release.
511 if (!upnpControlPoint
.WanServiceDetected()) {
512 // This condition can be used to suspend the parse
515 //#warning Delete this code when m_WanService is no longer used.
516 const_cast<CUPnPControlPoint
&>(upnpControlPoint
).SetWanService(this);
519 msg
<< "WAN Service Detected: '" <<
520 m_serviceType
<< "'.";
521 AddDebugLogLineC(logUPnP
, msg
);
523 const_cast<CUPnPControlPoint
&>(upnpControlPoint
).Subscribe(*this);
525 //#warning Delete this code on release.
528 msg
<< "WAN service detected again: '" <<
530 "'. Will only use the first instance.";
531 AddDebugLogLineC(logUPnP
, msg
);
536 msg
<< "Uninteresting service detected: '" <<
537 m_serviceType
<< "'. Ignoring.";
538 AddDebugLogLineC(logUPnP
, msg
);
543 CUPnPService::~CUPnPService()
548 bool CUPnPService::Execute(
549 const std::string
&ActionName
,
550 const std::vector
<CUPnPArgumentValue
> &ArgValue
) const
552 std::ostringstream msg
;
553 if (m_SCPD
.get() == nullptr) {
554 msg
<< "Service without SCPD Document, cannot execute action '" << ActionName
<<
555 "' for service '" << GetServiceType() << "'.";
556 AddDebugLogLineN(logUPnP
, msg
);
559 std::ostringstream
msgAction("Sending action ");
560 // Check for correct action name
561 ActionList::const_iterator itAction
=
562 m_SCPD
->GetActionList().find(ActionName
);
563 if (itAction
== m_SCPD
->GetActionList().end()) {
564 msg
<< "Invalid action name '" << ActionName
<<
565 "' for service '" << GetServiceType() << "'.";
566 AddDebugLogLineN(logUPnP
, msg
);
569 msgAction
<< ActionName
<< "(";
570 bool firstTime
= true;
571 // Check for correct Argument/Value pairs
572 const CUPnPAction
&action
= *(itAction
->second
);
573 for (unsigned int i
= 0; i
< ArgValue
.size(); ++i
) {
574 ArgumentList::const_iterator itArg
=
575 action
.GetArgumentList().find(ArgValue
[i
].GetArgument());
576 if (itArg
== action
.GetArgumentList().end()) {
577 msg
<< "Invalid argument name '" << ArgValue
[i
].GetArgument() <<
578 "' for action '" << action
.GetName() <<
579 "' for service '" << GetServiceType() << "'.";
580 AddDebugLogLineN(logUPnP
, msg
);
583 const CUPnPArgument
&argument
= *(itArg
->second
);
584 if (tolower(argument
.GetDirection()[0]) != 'i' ||
585 tolower(argument
.GetDirection()[1]) != 'n') {
586 msg
<< "Invalid direction for argument '" <<
587 ArgValue
[i
].GetArgument() <<
588 "' for action '" << action
.GetName() <<
589 "' for service '" << GetServiceType() << "'.";
590 AddDebugLogLineN(logUPnP
, msg
);
593 const std::string relatedStateVariableName
=
594 argument
.GetRelatedStateVariable();
595 if (!relatedStateVariableName
.empty()) {
596 ServiceStateTable::const_iterator itSVT
=
597 m_SCPD
->GetServiceStateTable().
598 find(relatedStateVariableName
);
599 if (itSVT
== m_SCPD
->GetServiceStateTable().end()) {
600 msg
<< "Inconsistent Service State Table, did not find '" <<
601 relatedStateVariableName
<<
602 "' for argument '" << argument
.GetName() <<
603 "' for action '" << action
.GetName() <<
604 "' for service '" << GetServiceType() << "'.";
605 AddDebugLogLineN(logUPnP
, msg
);
608 const CUPnPStateVariable
&stateVariable
= *(itSVT
->second
);
609 if ( !stateVariable
.GetAllowedValueList().empty() &&
610 stateVariable
.GetAllowedValueList().find(ArgValue
[i
].GetValue()) ==
611 stateVariable
.GetAllowedValueList().end()) {
612 msg
<< "Value not allowed '" << ArgValue
[i
].GetValue() <<
613 "' for state variable '" << relatedStateVariableName
<<
614 "' for argument '" << argument
.GetName() <<
615 "' for action '" << action
.GetName() <<
616 "' for service '" << GetServiceType() << "'.";
617 AddDebugLogLineN(logUPnP
, msg
);
627 ArgValue
[i
].GetArgument() <<
629 ArgValue
[i
].GetValue() <<
633 AddDebugLogLineN(logUPnP
, msgAction
);
634 // Everything is ok, make the action
635 IXML_Document
*ActionDoc
= NULL
;
636 if (!ArgValue
.empty()) {
637 for (unsigned int i
= 0; i
< ArgValue
.size(); ++i
) {
638 int ret
= UpnpAddToAction(
640 action
.GetName().c_str(),
641 GetServiceType().c_str(),
642 ArgValue
[i
].GetArgument().c_str(),
643 ArgValue
[i
].GetValue().c_str());
644 if (ret
!= UPNP_E_SUCCESS
) {
645 UPnP::ProcessErrorMessage(
646 "UpnpAddToAction", ret
, NULL
, NULL
);
651 ActionDoc
= UpnpMakeAction(
652 action
.GetName().c_str(),
653 GetServiceType().c_str(),
656 msg
<< "Error: UpnpMakeAction returned NULL.";
657 AddDebugLogLineN(logUPnP
, msg
);
662 // Send the action asynchronously
664 m_UPnPControlPoint
.GetUPnPClientHandle(),
665 GetAbsControlURL().c_str(),
666 GetServiceType().c_str(),
668 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
673 // Send the action synchronously
674 IXML_Document
*RespDoc
= NULL
;
675 int ret
= UpnpSendAction(
676 m_UPnPControlPoint
.GetUPnPClientHandle(),
677 GetAbsControlURL().c_str(),
678 GetServiceType().c_str(),
679 NULL
, ActionDoc
, &RespDoc
);
680 if (ret
!= UPNP_E_SUCCESS
) {
681 UPnP::ProcessErrorMessage(
682 "UpnpSendAction", ret
, NULL
, RespDoc
);
683 IXML::Document::Free(ActionDoc
);
684 IXML::Document::Free(RespDoc
);
687 IXML::Document::Free(ActionDoc
);
689 // Check the response document
690 UPnP::ProcessActionResponse(RespDoc
, action
.GetName());
692 // Free the response document
693 IXML::Document::Free(RespDoc
);
699 const std::string
CUPnPService::GetStateVariable(
700 const std::string
&stateVariableName
) const
702 std::ostringstream msg
;
704 int ret
= UpnpGetServiceVarStatus(
705 m_UPnPControlPoint
.GetUPnPClientHandle(),
706 GetAbsControlURL().c_str(),
707 stateVariableName
.c_str(),
709 if (ret
!= UPNP_E_SUCCESS
) {
710 msg
<< "GetStateVariable(\"" <<
712 "\"): in a call to UpnpGetServiceVarStatus";
713 UPnP::ProcessErrorMessage(
714 msg
.str(), ret
, StVarVal
, NULL
);
715 return stdEmptyString
;
717 msg
<< "GetStateVariable: " <<
722 AddDebugLogLineN(logUPnP
, msg
);
727 CUPnPDevice::CUPnPDevice(
728 const CUPnPControlPoint
&upnpControlPoint
,
729 IXML_Element
*device
,
730 const std::string
&URLBase
)
732 m_DeviceList(upnpControlPoint
, device
, URLBase
),
733 m_ServiceList(upnpControlPoint
, device
, URLBase
),
734 m_deviceType (IXML::Element::GetChildValueByTag(device
, "deviceType")),
735 m_friendlyName (IXML::Element::GetChildValueByTag(device
, "friendlyName")),
736 m_manufacturer (IXML::Element::GetChildValueByTag(device
, "manufacturer")),
737 m_manufacturerURL (IXML::Element::GetChildValueByTag(device
, "manufacturerURL")),
738 m_modelDescription (IXML::Element::GetChildValueByTag(device
, "modelDescription")),
739 m_modelName (IXML::Element::GetChildValueByTag(device
, "modelName")),
740 m_modelNumber (IXML::Element::GetChildValueByTag(device
, "modelNumber")),
741 m_modelURL (IXML::Element::GetChildValueByTag(device
, "modelURL")),
742 m_serialNumber (IXML::Element::GetChildValueByTag(device
, "serialNumber")),
743 m_UDN (IXML::Element::GetChildValueByTag(device
, "UDN")),
744 m_UPC (IXML::Element::GetChildValueByTag(device
, "UPC")),
745 m_presentationURL (IXML::Element::GetChildValueByTag(device
, "presentationURL"))
747 std::ostringstream msg
;
748 int presURLlen
= strlen(URLBase
.c_str()) +
749 strlen(m_presentationURL
.c_str()) + 2;
750 std::vector
<char> vpresURL(presURLlen
);
751 char* presURL
= &vpresURL
[0];
752 int errcode
= UpnpResolveURL(
754 m_presentationURL
.c_str(),
756 if (errcode
!= UPNP_E_SUCCESS
) {
757 msg
<< "Error generating presentationURL from " <<
758 "|" << URLBase
<< "|" <<
759 m_presentationURL
<< "|.";
760 AddDebugLogLineN(logUPnP
, msg
);
762 m_presentationURL
= presURL
;
766 msg
<< "\n Device: " <<
767 "\n friendlyName: " << m_friendlyName
<<
768 "\n deviceType: " << m_deviceType
<<
769 "\n manufacturer: " << m_manufacturer
<<
770 "\n manufacturerURL: " << m_manufacturerURL
<<
771 "\n modelDescription: " << m_modelDescription
<<
772 "\n modelName: " << m_modelName
<<
773 "\n modelNumber: " << m_modelNumber
<<
774 "\n modelURL: " << m_modelURL
<<
775 "\n serialNumber: " << m_serialNumber
<<
776 "\n UDN: " << m_UDN
<<
777 "\n UPC: " << m_UPC
<<
778 "\n presentationURL: " << m_presentationURL
;
779 AddDebugLogLineN(logUPnP
, msg
);
783 CUPnPRootDevice::CUPnPRootDevice(
784 const CUPnPControlPoint
&upnpControlPoint
,
785 IXML_Element
*rootDevice
,
786 const std::string
&OriginalURLBase
,
787 const std::string
&FixedURLBase
,
788 const char *location
,
791 CUPnPDevice(upnpControlPoint
, rootDevice
, FixedURLBase
),
792 m_URLBase(OriginalURLBase
),
793 m_location(location
),
796 std::ostringstream msg
;
798 "\n Root Device: " <<
799 "\n URLBase: " << m_URLBase
<<
800 "\n Fixed URLBase: " << FixedURLBase
<<
801 "\n location: " << m_location
<<
802 "\n expires: " << m_expires
;
803 AddDebugLogLineN(logUPnP
, msg
);
807 CUPnPControlPoint
*CUPnPControlPoint::s_CtrlPoint
= NULL
;
810 CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort
)
812 m_UPnPClientHandle(),
815 m_ActivePortMappingsMap(),
816 m_RootDeviceListMutex(),
817 m_IGWDeviceDetected(false),
822 // Null string at first
823 std::ostringstream msg
;
825 // Declare those here to avoid
826 // "jump to label ‘error’ [-fpermissive] crosses initialization
827 // of ‘char* ipAddress’"
833 ret
= UpnpInit2(0, udpPort
);
834 if (ret
!= UPNP_E_SUCCESS
) {
835 msg
<< "error(UpnpInit2): Error code ";
838 port
= UpnpGetServerPort();
839 ipAddress
= UpnpGetServerIpAddress();
840 msg
<< "bound to " << ipAddress
<< ":" <<
842 AddDebugLogLineN(logUPnP
, msg
);
844 ret
= UpnpRegisterClient(
845 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
847 &m_UPnPClientHandle
);
848 if (ret
!= UPNP_E_SUCCESS
) {
849 msg
<< "error(UpnpRegisterClient): Error registering callback: ";
853 // We could ask for just the right device here. If the root device
854 // contains the device we want, it will respond with the full XML doc,
855 // including the root device and every sub-device it has.
857 // But let's find out what we have in our network by calling UPnP::ROOT_DEVICE.
859 // We should not search twice, because this will produce two
860 // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
862 ret
= UpnpSearchAsync(m_UPnPClientHandle
, 3, UPnP::ROOT_DEVICE
.c_str(), NULL
);
863 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, UPnP::Device::IGW.c_str(), this);
864 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, UPnP::Device::LAN.c_str(), this);
865 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, UPnP::Device::WAN_Connection.c_str(), this);
866 if (ret
!= UPNP_E_SUCCESS
) {
867 msg
<< "error(UpnpSearchAsync): Error sending search request: ";
871 // Wait for the UPnP initialization to complete.
873 // Lock the search timeout mutex
874 m_WaitForSearchTimeoutMutex
.Lock();
876 // Lock it again, so that we block. Unlocking will only happen
877 // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
879 CUPnPMutexLocker
lock(m_WaitForSearchTimeoutMutex
);
886 msg
<< ret
<< ": " << UpnpGetErrorMessage(ret
) << ".";
887 throw CUPnPException(msg
);
891 CUPnPControlPoint::~CUPnPControlPoint()
893 for( RootDeviceMap::iterator it
= m_RootDeviceMap
.begin();
894 it
!= m_RootDeviceMap
.end();
900 UpnpUnRegisterClient(m_UPnPClientHandle
);
905 bool CUPnPControlPoint::AddPortMappings(
906 std::vector
<CUPnPPortMapping
> &upnpPortMapping
)
908 std::ostringstream msg
;
909 if (!WanServiceDetected()) {
910 msg
<< "UPnP Error: "
911 "CUPnPControlPoint::AddPortMapping: "
912 "WAN Service not detected.";
913 AddDebugLogLineC(logUPnP
, msg
);
917 int n
= upnpPortMapping
.size();
920 // Check the number of port mappings before
921 std::istringstream
PortMappingNumberOfEntries(
922 m_WanService
->GetStateVariable(
923 "PortMappingNumberOfEntries"));
924 unsigned long oldNumberOfEntries
;
925 PortMappingNumberOfEntries
>> oldNumberOfEntries
;
927 // Add the enabled port mappings
928 for (int i
= 0; i
< n
; ++i
) {
929 if (upnpPortMapping
[i
].getEnabled() == "1") {
930 // Add the mapping to the control point
931 // active mappings list
932 m_ActivePortMappingsMap
[upnpPortMapping
[i
].getKey()] =
935 // Add the port mapping
936 PrivateAddPortMapping(upnpPortMapping
[i
]);
940 // Test some variables, this is deprecated, might not work
942 m_WanService
->GetStateVariable("ConnectionType");
943 m_WanService
->GetStateVariable("PossibleConnectionTypes");
944 m_WanService
->GetStateVariable("ConnectionStatus");
945 m_WanService
->GetStateVariable("Uptime");
946 m_WanService
->GetStateVariable("LastConnectionError");
947 m_WanService
->GetStateVariable("RSIPAvailable");
948 m_WanService
->GetStateVariable("NATEnabled");
949 m_WanService
->GetStateVariable("ExternalIPAddress");
950 m_WanService
->GetStateVariable("PortMappingNumberOfEntries");
951 m_WanService
->GetStateVariable("PortMappingLeaseDuration");
954 std::vector
<CUPnPArgumentValue
> argval
;
956 m_WanService
->Execute("GetStatusInfo", argval
);
959 // These do not work. Their value must be requested for a
960 // specific port mapping.
961 m_WanService
->GetStateVariable("PortMappingEnabled");
962 m_WanService
->GetStateVariable("RemoteHost");
963 m_WanService
->GetStateVariable("ExternalPort");
964 m_WanService
->GetStateVariable("InternalPort");
965 m_WanService
->GetStateVariable("PortMappingProtocol");
966 m_WanService
->GetStateVariable("InternalClient");
967 m_WanService
->GetStateVariable("PortMappingDescription");
972 msg
<< "CUPnPControlPoint::AddPortMappings: "
973 "m_ActivePortMappingsMap.size() == " <<
974 m_ActivePortMappingsMap
.size();
975 AddDebugLogLineN(logUPnP
, msg
);
977 // Not very good, must find a better test
978 PortMappingNumberOfEntries
.str(
979 m_WanService
->GetStateVariable(
980 "PortMappingNumberOfEntries"));
981 unsigned long newNumberOfEntries
;
982 PortMappingNumberOfEntries
>> newNumberOfEntries
;
983 ok
= newNumberOfEntries
- oldNumberOfEntries
== 4;
989 void CUPnPControlPoint::RefreshPortMappings()
991 for ( PortMappingMap::iterator it
= m_ActivePortMappingsMap
.begin();
992 it
!= m_ActivePortMappingsMap
.end();
994 PrivateAddPortMapping(it
->second
);
998 m_WanService
->GetStateVariable("PortMappingNumberOfEntries");
1002 bool CUPnPControlPoint::PrivateAddPortMapping(
1003 CUPnPPortMapping
&upnpPortMapping
)
1005 // Get an IP address. The UPnP server one must do.
1006 std::string
ipAddress(UpnpGetServerIpAddress());
1008 // Start building the action
1009 std::string
actionName("AddPortMapping");
1010 std::vector
<CUPnPArgumentValue
> argval(8);
1012 // Action parameters
1013 argval
[0].SetArgument("NewRemoteHost");
1014 argval
[0].SetValue("");
1015 argval
[1].SetArgument("NewExternalPort");
1016 argval
[1].SetValue(upnpPortMapping
.getPort());
1017 argval
[2].SetArgument("NewProtocol");
1018 argval
[2].SetValue(upnpPortMapping
.getProtocol());
1019 argval
[3].SetArgument("NewInternalPort");
1020 argval
[3].SetValue(upnpPortMapping
.getPort());
1021 argval
[4].SetArgument("NewInternalClient");
1022 argval
[4].SetValue(ipAddress
);
1023 argval
[5].SetArgument("NewEnabled");
1024 argval
[5].SetValue("1");
1025 argval
[6].SetArgument("NewPortMappingDescription");
1026 argval
[6].SetValue(upnpPortMapping
.getDescription());
1027 argval
[7].SetArgument("NewLeaseDuration");
1028 argval
[7].SetValue("0");
1032 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1033 it
!= m_ServiceMap
.end(); ++it
) {
1034 ret
&= it
->second
->Execute(actionName
, argval
);
1041 bool CUPnPControlPoint::DeletePortMappings(
1042 std::vector
<CUPnPPortMapping
> &upnpPortMapping
)
1044 std::ostringstream msg
;
1045 if (!WanServiceDetected()) {
1046 msg
<< "UPnP Error: "
1047 "CUPnPControlPoint::DeletePortMapping: "
1048 "WAN Service not detected.";
1049 AddDebugLogLineC(logUPnP
, msg
);
1053 int n
= upnpPortMapping
.size();
1056 // Check the number of port mappings before
1057 std::istringstream
PortMappingNumberOfEntries(
1058 m_WanService
->GetStateVariable(
1059 "PortMappingNumberOfEntries"));
1060 unsigned long oldNumberOfEntries
;
1061 PortMappingNumberOfEntries
>> oldNumberOfEntries
;
1063 // Delete the enabled port mappings
1064 for (int i
= 0; i
< n
; ++i
) {
1065 if (upnpPortMapping
[i
].getEnabled() == "1") {
1066 // Delete the mapping from the control point
1067 // active mappings list
1068 PortMappingMap::iterator it
=
1069 m_ActivePortMappingsMap
.find(
1070 upnpPortMapping
[i
].getKey());
1071 if (it
!= m_ActivePortMappingsMap
.end()) {
1072 m_ActivePortMappingsMap
.erase(it
);
1074 msg
<< "UPnP Error: "
1075 "CUPnPControlPoint::DeletePortMapping: "
1076 "Mapping was not found in the active "
1078 AddDebugLogLineC(logUPnP
, msg
);
1081 // Delete the port mapping
1082 PrivateDeletePortMapping(upnpPortMapping
[i
]);
1088 msg
<< "CUPnPControlPoint::DeletePortMappings: "
1089 "m_ActivePortMappingsMap.size() == " <<
1090 m_ActivePortMappingsMap
.size();
1091 AddDebugLogLineN(logUPnP
, msg
);
1093 // Not very good, must find a better test
1094 PortMappingNumberOfEntries
.str(
1095 m_WanService
->GetStateVariable(
1096 "PortMappingNumberOfEntries"));
1097 unsigned long newNumberOfEntries
;
1098 PortMappingNumberOfEntries
>> newNumberOfEntries
;
1099 ok
= oldNumberOfEntries
- newNumberOfEntries
== 4;
1105 bool CUPnPControlPoint::PrivateDeletePortMapping(
1106 CUPnPPortMapping
&upnpPortMapping
)
1108 // Start building the action
1109 std::string
actionName("DeletePortMapping");
1110 std::vector
<CUPnPArgumentValue
> argval(3);
1112 // Action parameters
1113 argval
[0].SetArgument("NewRemoteHost");
1114 argval
[0].SetValue("");
1115 argval
[1].SetArgument("NewExternalPort");
1116 argval
[1].SetValue(upnpPortMapping
.getPort());
1117 argval
[2].SetArgument("NewProtocol");
1118 argval
[2].SetValue(upnpPortMapping
.getProtocol());
1122 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1123 it
!= m_ServiceMap
.end(); ++it
) {
1124 ret
&= it
->second
->Execute(actionName
, argval
);
1131 // This function is static
1132 #if UPNP_VERSION >= 10800
1133 int CUPnPControlPoint::Callback(Upnp_EventType_e EventType
, const void *Event
, void * /*Cookie*/)
1135 int CUPnPControlPoint::Callback(Upnp_EventType EventType
, void *Event
, void * /*Cookie*/)
1138 std::ostringstream msg
;
1139 std::ostringstream msg2
;
1140 // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
1141 // happen with a wrong cookie and... boom!
1142 // CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
1143 CUPnPControlPoint
*upnpCP
= CUPnPControlPoint::s_CtrlPoint
;
1145 //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
1146 switch (EventType
) {
1147 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
:
1148 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
1149 msg
<< "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): ";
1150 msg2
<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
1152 case UPNP_DISCOVERY_SEARCH_RESULT
: {
1153 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n");
1154 msg
<< "error(UPNP_DISCOVERY_SEARCH_RESULT): ";
1155 msg2
<< "UPNP_DISCOVERY_SEARCH_RESULT: ";
1158 #if UPNP_VERSION >= 10800
1159 UpnpDiscovery
*d_event
= (UpnpDiscovery
*)Event
;
1161 struct Upnp_Discovery
*d_event
= (struct Upnp_Discovery
*)Event
;
1163 IXML_Document
*doc
= NULL
;
1164 #if UPNP_VERSION >= 10800
1165 int errCode
= UpnpDiscovery_get_ErrCode(d_event
);
1166 if (errCode
!= UPNP_E_SUCCESS
) {
1167 msg
<< UpnpGetErrorMessage(errCode
) << ".";
1170 if (d_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1171 msg
<< UpnpGetErrorMessage(d_event
->ErrCode
) << ".";
1173 AddDebugLogLineC(logUPnP
, msg
);
1175 // Get the XML tree device description in doc
1176 #if UPNP_VERSION >= 10800
1177 const char *location
= UpnpDiscovery_get_Location_cstr(d_event
);
1178 int ret
= UpnpDownloadXmlDoc(location
, &doc
);
1180 ret
= UpnpDownloadXmlDoc(d_event
->Location
, &doc
);
1182 if (ret
!= UPNP_E_SUCCESS
) {
1183 msg
<< "Error retrieving device description from " <<
1184 #if UPNP_VERSION >= 10800
1187 d_event
->Location
<< ": " <<
1189 UpnpGetErrorMessage(ret
) <<
1191 AddDebugLogLineC(logUPnP
, msg
);
1193 msg2
<< "Retrieving device description from " <<
1194 #if UPNP_VERSION >= 10800
1197 d_event
->Location
<< ".";
1199 AddDebugLogLineN(logUPnP
, msg2
);
1202 // Get the root node
1203 IXML_Element
*root
= IXML::Document::GetRootElement(doc
);
1204 // Extract the URLBase
1205 const std::string urlBase
= IXML::Element::GetChildValueByTag(root
, "URLBase");
1206 // Get the root device
1207 IXML_Element
*rootDevice
= IXML::Element::GetFirstChildByTag(root
, "device");
1208 // Extract the deviceType
1209 std::string
devType(IXML::Element::GetChildValueByTag(rootDevice
, "deviceType"));
1210 // Only add device if it is an InternetGatewayDevice
1211 if (stdStringIsEqualCI(devType
, UPnP::Device::IGW
)) {
1212 // This condition can be used to auto-detect
1213 // the UPnP device we are interested in.
1214 // Obs.: Don't block the entry here on this
1215 // condition! There may be more than one device,
1216 // and the first that enters may not be the one
1217 // we are interested in!
1218 upnpCP
->SetIGWDeviceDetected(true);
1219 // Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
1220 // we don't want to spam our logs.
1221 if (EventType
!= UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
) {
1222 msg
.str("Internet Gateway Device Detected.");
1223 AddDebugLogLineC(logUPnP
, msg
);
1225 // Add the root device to our list
1226 #if UPNP_VERSION >= 10800
1227 int expires
= UpnpDiscovery_get_Expires(d_event
);
1228 upnpCP
->AddRootDevice(rootDevice
, urlBase
,
1231 upnpCP
->AddRootDevice(rootDevice
, urlBase
,
1232 d_event
->Location
, d_event
->Expires
);
1235 // Free the XML doc tree
1236 IXML::Document::Free(doc
);
1240 case UPNP_DISCOVERY_SEARCH_TIMEOUT
: {
1241 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1243 msg
<< "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1244 AddDebugLogLineN(logUPnP
, msg
);
1246 // Unlock the search timeout mutex
1247 upnpCP
->m_WaitForSearchTimeoutMutex
.Unlock();
1251 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE
: {
1252 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
1253 // UPnP Device Removed
1254 #if UPNP_VERSION >= 10800
1255 UpnpDiscovery
*dab_event
= (UpnpDiscovery
*)Event
;
1256 int errCode
= UpnpDiscovery_get_ErrCode(dab_event
);
1257 if (errCode
!= UPNP_E_SUCCESS
) {
1259 struct Upnp_Discovery
*dab_event
= (struct Upnp_Discovery
*)Event
;
1260 if (dab_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1262 msg
<< "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
1263 #if UPNP_VERSION >= 10800
1264 UpnpGetErrorMessage(errCode
) <<
1266 UpnpGetErrorMessage(dab_event
->ErrCode
) <<
1269 AddDebugLogLineC(logUPnP
, msg
);
1271 #if UPNP_VERSION >= 10800
1272 std::string devType
= UpnpDiscovery_get_DeviceType_cstr(dab_event
);
1274 std::string devType
= dab_event
->DeviceType
;
1276 // Check for an InternetGatewayDevice and removes it from the list
1278 std::transform(devType
.begin(), devType
.end(), devType
.begin(), tolower
);
1280 if (stdStringIsEqualCI(devType
, UPnP::Device::IGW
)) {
1281 #if UPNP_VERSION >= 10800
1282 const char *deviceID
=
1283 UpnpDiscovery_get_DeviceID_cstr(dab_event
);
1284 upnpCP
->RemoveRootDevice(deviceID
);
1286 upnpCP
->RemoveRootDevice(dab_event
->DeviceId
);
1291 case UPNP_EVENT_RECEIVED
: {
1292 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1294 #if UPNP_VERSION >= 10800
1295 UpnpEvent
*e_event
= (UpnpEvent
*)Event
;
1296 int eventKey
= UpnpEvent_get_EventKey(e_event
);
1297 IXML_Document
*changedVariables
=
1298 UpnpEvent_get_ChangedVariables(e_event
);
1299 const std::string sid
= UpnpEvent_get_SID_cstr(e_event
);
1301 struct Upnp_Event
*e_event
= (struct Upnp_Event
*)Event
;
1302 const std::string Sid
= e_event
->Sid
;
1305 #if UPNP_VERSION >= 10800
1306 upnpCP
->OnEventReceived(sid
, eventKey
, changedVariables
);
1308 upnpCP
->OnEventReceived(Sid
, e_event
->EventKey
, e_event
->ChangedVariables
);
1312 case UPNP_EVENT_SUBSCRIBE_COMPLETE
:
1313 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
1314 msg
<< "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): ";
1315 goto upnpEventRenewalComplete
;
1316 case UPNP_EVENT_UNSUBSCRIBE_COMPLETE
:
1317 //fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
1318 msg
<< "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): ";
1319 goto upnpEventRenewalComplete
;
1320 case UPNP_EVENT_RENEWAL_COMPLETE
: {
1321 //fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n");
1322 msg
<< "error(UPNP_EVENT_RENEWAL_COMPLETE): ";
1323 upnpEventRenewalComplete
:
1324 #if UPNP_VERSION >= 10800
1325 UpnpEventSubscribe
*es_event
= (UpnpEventSubscribe
*)Event
;
1326 int errCode
= UpnpEventSubscribe_get_ErrCode(es_event
);
1327 if (errCode
!= UPNP_E_SUCCESS
) {
1329 struct Upnp_Event_Subscribe
*es_event
=
1330 (struct Upnp_Event_Subscribe
*)Event
;
1331 if (es_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1333 msg
<< "Error in Event Subscribe Callback";
1334 #if UPNP_VERSION >= 10800
1335 UPnP::ProcessErrorMessage(msg
.str(), errCode
, NULL
, NULL
);
1337 UPnP::ProcessErrorMessage(
1338 msg
.str(), es_event
->ErrCode
, NULL
, NULL
);
1342 #if UPNP_VERSION >= 10800
1344 const UpnpString
*publisherUrl
=
1345 UpnpEventSubscribe_get_PublisherUrl(es_event
);
1346 const char *sid
= UpnpEvent_get_SID_cstr(es_event
);
1347 int timeOut
= UpnpEvent_get_TimeOut(es_event
);
1348 TvCtrlPointHandleSubscribeUpdate(
1349 publisherUrl
, sid
, timeOut
);
1351 TvCtrlPointHandleSubscribeUpdate(
1352 GET_UPNP_STRING(es_event
->PublisherUrl
),
1354 es_event
->TimeOut
);
1360 case UPNP_EVENT_AUTORENEWAL_FAILED
:
1361 //fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n");
1362 msg
<< "error(UPNP_EVENT_AUTORENEWAL_FAILED): ";
1363 msg2
<< "UPNP_EVENT_AUTORENEWAL_FAILED: ";
1364 goto upnpEventSubscriptionExpired
;
1365 case UPNP_EVENT_SUBSCRIPTION_EXPIRED
: {
1366 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
1367 msg
<< "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): ";
1368 msg2
<< "UPNP_EVENT_SUBSCRIPTION_EXPIRED: ";
1369 upnpEventSubscriptionExpired
:
1370 #if UPNP_VERSION >= 10800
1371 UpnpEventSubscribe
*es_event
= (UpnpEventSubscribe
*)Event
;
1373 struct Upnp_Event_Subscribe
*es_event
=
1374 (struct Upnp_Event_Subscribe
*)Event
;
1377 memset(newSID
, 0, sizeof(Upnp_SID
));
1379 #if UPNP_VERSION >= 10800
1380 const char *publisherUrl
=
1381 UpnpEventSubscribe_get_PublisherUrl_cstr(es_event
);
1383 int ret
= UpnpSubscribe(
1384 upnpCP
->m_UPnPClientHandle
,
1385 #if UPNP_VERSION >= 10800
1388 GET_UPNP_STRING(es_event
->PublisherUrl
),
1392 if (ret
!= UPNP_E_SUCCESS
) {
1393 msg
<< "Error Subscribing to EventURL";
1394 #if UPNP_VERSION >= 10800
1395 int errCode
= UpnpEventSubscribe_get_ErrCode(es_event
);
1397 UPnP::ProcessErrorMessage(
1398 #if UPNP_VERSION >= 10800
1399 msg
.str(), errCode
, NULL
, NULL
);
1401 msg
.str(), es_event
->ErrCode
, NULL
, NULL
);
1404 ServiceMap::iterator it
=
1405 #if UPNP_VERSION >= 10800
1406 upnpCP
->m_ServiceMap
.find(publisherUrl
);
1408 upnpCP
->m_ServiceMap
.find(GET_UPNP_STRING(es_event
->PublisherUrl
));
1410 if (it
!= upnpCP
->m_ServiceMap
.end()) {
1411 CUPnPService
&service
= *(it
->second
);
1412 service
.SetTimeout(TimeOut
);
1413 service
.SetSID(newSID
);
1414 msg2
<< "Re-subscribed to EventURL '" <<
1415 #if UPNP_VERSION >= 10800
1418 GET_UPNP_STRING(es_event
->PublisherUrl
) <<
1420 "' with SID == '" <<
1422 AddDebugLogLineC(logUPnP
, msg2
);
1423 // In principle, we should test to see if the
1424 // service is the same. But here we only have one
1426 upnpCP
->RefreshPortMappings();
1428 msg
<< "Error: did not find service " <<
1429 newSID
<< " in the service map.";
1430 AddDebugLogLineC(logUPnP
, msg
);
1435 case UPNP_CONTROL_ACTION_COMPLETE
: {
1436 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n");
1437 // This is here if we choose to do this asynchronously
1438 #if UPNP_VERSION >= 10800
1439 UpnpActionComplete
*a_event
= (UpnpActionComplete
*)Event
;
1440 int errCode
= UpnpActionComplete_get_ErrCode(a_event
);
1441 IXML_Document
*actionResult
=
1442 UpnpActionComplete_get_ActionResult(a_event
);
1443 if (errCode
!= UPNP_E_SUCCESS
) {
1445 struct Upnp_Action_Complete
*a_event
=
1446 (struct Upnp_Action_Complete
*)Event
;
1447 if (a_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1449 UPnP::ProcessErrorMessage(
1450 "UpnpSendActionAsync",
1451 #if UPNP_VERSION >= 10800
1455 a_event
->ErrCode
, NULL
,
1456 a_event
->ActionResult
);
1459 // Check the response document
1460 UPnP::ProcessActionResponse(
1461 #if UPNP_VERSION >= 10800
1464 a_event
->ActionResult
,
1466 "<UpnpSendActionAsync>");
1468 /* No need for any processing here, just print out results.
1469 * Service state table updates are handled by events.
1473 case UPNP_CONTROL_GET_VAR_COMPLETE
: {
1474 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n");
1475 msg
<< "error(UPNP_CONTROL_GET_VAR_COMPLETE): ";
1476 #if UPNP_VERSION >= 10800
1477 UpnpStateVarComplete
*sv_event
= (UpnpStateVarComplete
*)Event
;
1478 int errCode
= UpnpStateVarComplete_get_ErrCode(sv_event
);
1479 if (errCode
!= UPNP_E_SUCCESS
) {
1481 struct Upnp_State_Var_Complete
*sv_event
=
1482 (struct Upnp_State_Var_Complete
*)Event
;
1483 if (sv_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1485 msg
<< "m_UpnpGetServiceVarStatusAsync";
1486 UPnP::ProcessErrorMessage(
1487 #if UPNP_VERSION >= 10800
1488 msg
.str(), errCode
, NULL
, NULL
);
1490 msg
.str(), sv_event
->ErrCode
, NULL
, NULL
);
1494 // Warning: The use of UpnpGetServiceVarStatus and
1495 // UpnpGetServiceVarStatusAsync is deprecated by the
1497 #if UPNP_VERSION >= 10800
1498 const char *ctrlUrl
=
1499 UpnpStateVarComplete_get_CtrlUrl(sv_event
);
1500 const char *stateVarName
=
1501 UpnpStateVarComplete_get_StateVarName(sv_event
);
1502 const DOMString currentVal
=
1503 UpnpStateVarComplete_get_CurrentVal(sv_event
);
1504 TvCtrlPointHandleGetVar(
1505 ctrlUrl
, stateVarName
, currentVal
);
1507 TvCtrlPointHandleGetVar(
1509 sv_event
->StateVarName
,
1510 sv_event
->CurrentVal
);
1516 // ignore these cases, since this is not a device
1517 case UPNP_CONTROL_GET_VAR_REQUEST
:
1518 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n");
1519 msg
<< "error(UPNP_CONTROL_GET_VAR_REQUEST): ";
1520 goto eventSubscriptionRequest
;
1521 case UPNP_CONTROL_ACTION_REQUEST
:
1522 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n");
1523 msg
<< "error(UPNP_CONTROL_ACTION_REQUEST): ";
1524 goto eventSubscriptionRequest
;
1525 case UPNP_EVENT_SUBSCRIPTION_REQUEST
:
1526 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
1527 msg
<< "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): ";
1528 eventSubscriptionRequest
:
1529 msg
<< "This is not a UPnP Device, this is a UPnP Control Point, event ignored.";
1530 AddDebugLogLineC(logUPnP
, msg
);
1533 // Humm, this is not good, we forgot to handle something...
1535 "Callback: default... Unknown event:'%d', not good.\n",
1537 msg
<< "error(UPnP::Callback): Event not handled:'" <<
1539 fprintf(stderr
, "%s\n", msg
.str().c_str());
1540 AddDebugLogLineC(logUPnP
, msg
);
1541 // Better not throw in the callback. Who would catch it?
1542 //throw CUPnPException(msg);
1550 void CUPnPControlPoint::OnEventReceived(
1551 const std::string
&Sid
,
1553 IXML_Document
*ChangedVariablesDoc
)
1555 std::ostringstream msg
;
1556 msg
<< "UPNP_EVENT_RECEIVED:" <<
1557 "\n SID: " << Sid
<<
1558 "\n Key: " << EventKey
<<
1559 "\n Property list:";
1560 IXML_Element
*root
= IXML::Document::GetRootElement(ChangedVariablesDoc
);
1561 IXML_Element
*child
= IXML::Element::GetFirstChild(root
);
1564 IXML_Element
*child2
= IXML::Element::GetFirstChild(child
);
1565 const DOMString childTag
= IXML::Element::GetTag(child2
);
1566 std::string childValue
= IXML::Element::GetTextValue(child2
);
1570 child
= IXML::Element::GetNextSibling(child
);
1573 msg
<< "\n Empty property list.";
1575 AddDebugLogLineC(logUPnP
, msg
);
1579 void CUPnPControlPoint::AddRootDevice(
1580 IXML_Element
*rootDevice
, const std::string
&urlBase
,
1581 const char *location
, int expires
)
1583 // Lock the Root Device List
1584 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1586 // Root node's URLBase
1587 std::string
OriginalURLBase(urlBase
);
1588 std::string
FixedURLBase(OriginalURLBase
.empty() ?
1592 // Get the UDN (Unique Device Name)
1593 std::string
UDN(IXML::Element::GetChildValueByTag(rootDevice
, "UDN"));
1594 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1595 bool alreadyAdded
= it
!= m_RootDeviceMap
.end();
1597 // Just set the expires field
1598 it
->second
->SetExpires(expires
);
1600 // Add a new root device to the root device list
1601 CUPnPRootDevice
*upnpRootDevice
= new CUPnPRootDevice(
1603 OriginalURLBase
, FixedURLBase
,
1605 m_RootDeviceMap
[upnpRootDevice
->GetUDN()] = upnpRootDevice
;
1610 void CUPnPControlPoint::RemoveRootDevice(const char *udn
)
1612 // Lock the Root Device List
1613 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1616 std::string
UDN(udn
);
1617 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1618 if (it
!= m_RootDeviceMap
.end()) {
1620 m_RootDeviceMap
.erase(UDN
);
1625 void CUPnPControlPoint::Subscribe(CUPnPService
&service
)
1627 std::ostringstream msg
;
1629 IXML_Document
*scpdDoc
= NULL
;
1630 int errcode
= UpnpDownloadXmlDoc(
1631 service
.GetAbsSCPDURL().c_str(), &scpdDoc
);
1632 if (errcode
== UPNP_E_SUCCESS
) {
1633 // Get the root node of this service (the SCPD Document)
1634 IXML_Element
*scpdRoot
= IXML::Document::GetRootElement(scpdDoc
);
1635 CUPnPSCPD
*scpd
= new CUPnPSCPD(*this, scpdRoot
, service
.GetAbsSCPDURL());
1636 service
.SetSCPD(scpd
);
1637 IXML::Document::Free(scpdDoc
);
1638 m_ServiceMap
[service
.GetAbsEventSubURL()] = &service
;
1639 msg
<< "Successfully retrieved SCPD Document for service " <<
1640 service
.GetServiceType() << ", absEventSubURL: " <<
1641 service
.GetAbsEventSubURL() << ".";
1642 AddDebugLogLineC(logUPnP
, msg
);
1645 // Now try to subscribe to this service. If the subscription
1646 // is not successfull, we will not be notified about events,
1647 // but it may be possible to use the service anyway.
1648 errcode
= UpnpSubscribe(m_UPnPClientHandle
,
1649 service
.GetAbsEventSubURL().c_str(),
1650 service
.GetTimeoutAddr(),
1652 if (errcode
== UPNP_E_SUCCESS
) {
1653 msg
<< "Successfully subscribed to service " <<
1654 service
.GetServiceType() << ", absEventSubURL: " <<
1655 service
.GetAbsEventSubURL() << ".";
1656 AddDebugLogLineC(logUPnP
, msg
);
1658 msg
<< "Error subscribing to service " <<
1659 service
.GetServiceType() << ", absEventSubURL: " <<
1660 service
.GetAbsEventSubURL() << ", error: " <<
1661 UpnpGetErrorMessage(errcode
) << ".";
1665 msg
<< "Error getting SCPD Document from " <<
1666 service
.GetAbsSCPDURL() << ".";
1667 AddDebugLogLineC(logUPnP
, msg
);
1674 AddDebugLogLineC(logUPnP
, msg
);
1678 void CUPnPControlPoint::Unsubscribe(CUPnPService
&service
)
1680 ServiceMap::iterator it
= m_ServiceMap
.find(service
.GetAbsEventSubURL());
1681 if (it
!= m_ServiceMap
.end()) {
1682 m_ServiceMap
.erase(it
);
1683 UpnpUnSubscribe(m_UPnPClientHandle
, service
.GetSID());
1687 #endif /* ENABLE_UPNP */