2 // This file is part of the aMule Project.
4 // Copyright (c) 2004-2011 Marcelo Roberto Jimenez ( phoenix@amule.org )
5 // Copyright (c) 2006-2011 aMule Team ( admin@amule.org / http://www.amule.org )
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 # include "config.h" // Needed for ENABLE_UPNP
32 // check for broken Debian-hacked libUPnP
34 #ifdef STRING_H // defined in UpnpString.h Yes, I would have liked UPNPSTRING_H much better.
35 #define BROKEN_DEBIAN_LIBUPNP
40 #include <algorithm> // For transform()
42 #ifdef BROKEN_DEBIAN_LIBUPNP
43 #define GET_UPNP_STRING(a) UpnpString_get_String(a)
45 #define GET_UPNP_STRING(a) (a)
48 std::string stdEmptyString
;
50 const char s_argument
[] = "argument";
51 const char s_argumentList
[] = "argumentList";
52 const char s_action
[] = "action";
53 const char s_actionList
[] = "actionList";
54 const char s_allowedValue
[] = "allowedValue";
55 const char s_allowedValueList
[] = "allowedValueList";
56 const char s_stateVariable
[] = "stateVariable";
57 const char s_serviceStateTable
[] = "serviceStateTable";
58 const char s_service
[] = "service";
59 const char s_serviceList
[] = "serviceList";
60 const char s_device
[] = "device";
61 const char s_deviceList
[] = "deviceList";
64 * Case insensitive std::string comparison
66 bool stdStringIsEqualCI(const std::string
&s1
, const std::string
&s2
)
70 std::transform(ns1
.begin(), ns1
.end(), ns1
.begin(), tolower
);
71 std::transform(ns2
.begin(), ns2
.end(), ns2
.begin(), tolower
);
76 CUPnPPortMapping::CUPnPPortMapping(
78 const std::string
&protocol
,
80 const std::string
&description
)
84 m_enabled(enabled
? "1" : "0"),
85 m_description(description
),
88 std::ostringstream oss
;
91 m_key
= m_protocol
+ m_port
;
96 static const std::string
ROOT_DEVICE("upnp:rootdevice");
99 static const std::string
IGW("urn:schemas-upnp-org:device:InternetGatewayDevice:1");
100 static const std::string
WAN("urn:schemas-upnp-org:device:WANDevice:1");
101 static const std::string
WAN_Connection("urn:schemas-upnp-org:device:WANConnectionDevice:1");
102 static const std::string
LAN("urn:schemas-upnp-org:device:LANDevice:1");
106 static const std::string
Layer3_Forwarding("urn:schemas-upnp-org:service:Layer3Forwarding:1");
107 static const std::string
WAN_Common_Interface_Config("urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1");
108 static const std::string
WAN_IP_Connection("urn:schemas-upnp-org:service:WANIPConnection:1");
109 static const std::string
WAN_PPP_Connection("urn:schemas-upnp-org:service:WANPPPConnection:1");
112 static std::string
ProcessErrorMessage(
113 const std::string
&messsage
,
115 const DOMString errorString
,
118 std::ostringstream msg
;
119 if (errorString
== NULL
|| *errorString
== 0) {
120 errorString
= "Not available";
128 msg
<< e
.getErrorCode() <<
129 "', Error description :'" <<
130 e
.getErrorDescription() <<
134 "', Error description :'" <<
138 AddDebugLogLineN(logUPnP
, msg
);
142 ": UPnP SDK error: " <<
143 UpnpGetErrorMessage(errorCode
) <<
144 " (" << errorCode
<< ").";
145 AddDebugLogLineN(logUPnP
, msg
);
152 static void ProcessActionResponse(
153 IXML_Document
*RespDoc
,
154 const std::string
&actionName
)
156 std::ostringstream msg
;
158 IXML_Element
*root
= IXML::Document::GetRootElement(RespDoc
);
159 IXML_Element
*child
= IXML::Element::GetFirstChild(root
);
162 const DOMString childTag
= IXML::Element::GetTag(child
);
163 std::string childValue
= IXML::Element::GetTextValue(child
);
167 child
= IXML::Element::GetNextSibling(child
);
170 msg
<< "\n Empty response for action '" <<
173 AddDebugLogLineN(logUPnP
, msg
);
176 } /* namespace UPnP */
182 * \brief Returns the root node of a given document.
184 IXML_Element
*Document::GetRootElement(IXML_Document
*doc
)
186 return reinterpret_cast<IXML_Element
*>(ixmlNode_getFirstChild(&doc
->n
));
190 * \brief Frees the given document.
192 * \note Any nodes extracted via any other interface function will become
193 * invalid after this call unless explicitly cloned.
195 inline void Document::Free(IXML_Document
*doc
)
197 ixmlDocument_free(doc
);
203 * \brief Returns the first child of a given element.
205 IXML_Element
*GetFirstChild(IXML_Element
*parent
)
207 return reinterpret_cast<IXML_Element
*>(ixmlNode_getFirstChild(&parent
->n
));
213 * \brief Returns the next sibling of a given child.
215 IXML_Element
*GetNextSibling(IXML_Element
*child
)
217 return reinterpret_cast<IXML_Element
*>(ixmlNode_getNextSibling(&child
->n
));
222 * \brief Returns the element tag (name)
224 const DOMString
GetTag(IXML_Element
*element
)
226 return ixmlNode_getNodeName(&element
->n
);
231 * \brief Returns the TEXT node value of the current node.
233 const std::string
GetTextValue(IXML_Element
*element
)
236 return stdEmptyString
;
238 IXML_Node
*text
= ixmlNode_getFirstChild(&element
->n
);
239 const DOMString s
= ixmlNode_getNodeValue(text
);
250 * \brief Returns the TEXT node value of the first child matching tag.
252 const std::string
GetChildValueByTag(IXML_Element
*element
, const DOMString tag
)
254 return GetTextValue(GetFirstChildByTag(element
, tag
));
259 * \brief Returns the first child element that matches the requested tag or
262 IXML_Element
*GetFirstChildByTag(IXML_Element
*element
, const DOMString tag
)
264 if (!element
|| !tag
) {
268 IXML_Node
*child
= ixmlNode_getFirstChild(&element
->n
);
269 const DOMString childTag
= ixmlNode_getNodeName(child
);
270 while(child
&& childTag
&& strcmp(tag
, childTag
)) {
271 child
= ixmlNode_getNextSibling(child
);
272 childTag
= ixmlNode_getNodeName(child
);
275 return reinterpret_cast<IXML_Element
*>(child
);
280 * \brief Returns the next sibling element that matches the requested tag. Should be
281 * used with the return value of GetFirstChildByTag().
283 IXML_Element
*GetNextSiblingByTag(IXML_Element
*element
, const DOMString tag
)
285 if (!element
|| !tag
) {
289 IXML_Node
*child
= &element
->n
;
290 const DOMString childTag
= NULL
;
292 child
= ixmlNode_getNextSibling(child
);
293 childTag
= ixmlNode_getNodeName(child
);
294 } while(child
&& childTag
&& strcmp(tag
, childTag
));
296 return reinterpret_cast<IXML_Element
*>(child
);
300 const std::string
GetAttributeByTag(IXML_Element
*element
, const DOMString tag
)
302 IXML_NamedNodeMap
*NamedNodeMap
= ixmlNode_getAttributes(&element
->n
);
303 IXML_Node
*attribute
= ixmlNamedNodeMap_getNamedItem(NamedNodeMap
, tag
);
304 const DOMString s
= ixmlNode_getNodeValue(attribute
);
309 ixmlNamedNodeMap_free(NamedNodeMap
);
314 } /* namespace Element */
316 } /* namespace IXML */
319 CUPnPError::CUPnPError(IXML_Document
*errorDoc
)
321 m_root (IXML::Document::GetRootElement(errorDoc
)),
322 m_ErrorCode (IXML::Element::GetChildValueByTag(m_root
, "errorCode")),
323 m_ErrorDescription(IXML::Element::GetChildValueByTag(m_root
, "errorDescription"))
328 CUPnPArgument::CUPnPArgument(
329 const CUPnPControlPoint
&WXUNUSED(upnpControlPoint
),
330 IXML_Element
*argument
,
331 const std::string
&WXUNUSED(SCPDURL
))
333 m_name (IXML::Element::GetChildValueByTag(argument
, "name")),
334 m_direction (IXML::Element::GetChildValueByTag(argument
, "direction")),
335 m_retval (IXML::Element::GetFirstChildByTag(argument
, "retval")),
336 m_relatedStateVariable(IXML::Element::GetChildValueByTag(argument
, "relatedStateVariable"))
338 std::ostringstream msg
;
339 msg
<< "\n Argument:" <<
340 "\n name: " << m_name
<<
341 "\n direction: " << m_direction
<<
342 "\n retval: " << m_retval
<<
343 "\n relatedStateVariable: " << m_relatedStateVariable
;
344 AddDebugLogLineN(logUPnP
, msg
);
348 CUPnPAction::CUPnPAction(
349 const CUPnPControlPoint
&upnpControlPoint
,
350 IXML_Element
*action
,
351 const std::string
&SCPDURL
)
353 m_ArgumentList(upnpControlPoint
, action
, SCPDURL
),
354 m_name(IXML::Element::GetChildValueByTag(action
, "name"))
356 std::ostringstream msg
;
357 msg
<< "\n Action:" <<
358 "\n name: " << m_name
;
359 AddDebugLogLineN(logUPnP
, msg
);
363 CUPnPAllowedValue::CUPnPAllowedValue(
364 const CUPnPControlPoint
&WXUNUSED(upnpControlPoint
),
365 IXML_Element
*allowedValue
,
366 const std::string
&WXUNUSED(SCPDURL
))
368 m_allowedValue(IXML::Element::GetTextValue(allowedValue
))
370 std::ostringstream msg
;
371 msg
<< "\n AllowedValue:" <<
372 "\n allowedValue: " << m_allowedValue
;
373 AddDebugLogLineN(logUPnP
, msg
);
377 CUPnPStateVariable::CUPnPStateVariable(
378 const CUPnPControlPoint
&upnpControlPoint
,
379 IXML_Element
*stateVariable
,
380 const std::string
&SCPDURL
)
382 m_AllowedValueList(upnpControlPoint
, stateVariable
, SCPDURL
),
383 m_name (IXML::Element::GetChildValueByTag(stateVariable
, "name")),
384 m_dataType (IXML::Element::GetChildValueByTag(stateVariable
, "dataType")),
385 m_defaultValue(IXML::Element::GetChildValueByTag(stateVariable
, "defaultValue")),
386 m_sendEvents (IXML::Element::GetAttributeByTag (stateVariable
, "sendEvents"))
388 std::ostringstream msg
;
389 msg
<< "\n StateVariable:" <<
390 "\n name: " << m_name
<<
391 "\n dataType: " << m_dataType
<<
392 "\n defaultValue: " << m_defaultValue
<<
393 "\n sendEvents: " << m_sendEvents
;
394 AddDebugLogLineN(logUPnP
, msg
);
398 CUPnPSCPD::CUPnPSCPD(
399 const CUPnPControlPoint
&upnpControlPoint
,
401 const std::string
&SCPDURL
)
403 m_ActionList(upnpControlPoint
, scpd
, SCPDURL
),
404 m_ServiceStateTable(upnpControlPoint
, scpd
, SCPDURL
),
410 CUPnPArgumentValue::CUPnPArgumentValue()
418 CUPnPArgumentValue::CUPnPArgumentValue(
419 const std::string
&argument
, const std::string
&value
)
421 m_argument(argument
),
427 CUPnPService::CUPnPService(
428 const CUPnPControlPoint
&upnpControlPoint
,
429 IXML_Element
*service
,
430 const std::string
&URLBase
)
432 m_UPnPControlPoint(upnpControlPoint
),
433 m_serviceType(IXML::Element::GetChildValueByTag(service
, "serviceType")),
434 m_serviceId (IXML::Element::GetChildValueByTag(service
, "serviceId")),
435 m_SCPDURL (IXML::Element::GetChildValueByTag(service
, "SCPDURL")),
436 m_controlURL (IXML::Element::GetChildValueByTag(service
, "controlURL")),
437 m_eventSubURL(IXML::Element::GetChildValueByTag(service
, "eventSubURL")),
441 std::ostringstream msg
;
444 memset(m_SID
, 0 , sizeof(Upnp_SID
));
446 std::vector
<char> vscpdURL(URLBase
.length() + m_SCPDURL
.length() + 1);
447 char *scpdURL
= &vscpdURL
[0];
448 errcode
= UpnpResolveURL(
452 if( errcode
!= UPNP_E_SUCCESS
) {
453 msg
<< "Error generating scpdURL from " <<
454 "|" << URLBase
<< "|" <<
456 AddDebugLogLineN(logUPnP
, msg
);
458 m_absSCPDURL
= scpdURL
;
461 std::vector
<char> vcontrolURL(
462 URLBase
.length() + m_controlURL
.length() + 1);
463 char *controlURL
= &vcontrolURL
[0];
464 errcode
= UpnpResolveURL(
466 m_controlURL
.c_str(),
468 if( errcode
!= UPNP_E_SUCCESS
) {
469 msg
<< "Error generating controlURL from " <<
470 "|" << URLBase
<< "|" <<
471 m_controlURL
<< "|.";
472 AddDebugLogLineN(logUPnP
, msg
);
474 m_absControlURL
= controlURL
;
477 std::vector
<char> veventURL(
478 URLBase
.length() + m_eventSubURL
.length() + 1);
479 char *eventURL
= &veventURL
[0];
480 errcode
= UpnpResolveURL(
482 m_eventSubURL
.c_str(),
484 if( errcode
!= UPNP_E_SUCCESS
) {
485 msg
<< "Error generating eventURL from " <<
486 "|" << URLBase
<< "|" <<
487 m_eventSubURL
<< "|.";
488 AddDebugLogLineN(logUPnP
, msg
);
490 m_absEventSubURL
= eventURL
;
493 msg
<< "\n Service:" <<
494 "\n serviceType: " << m_serviceType
<<
495 "\n serviceId: " << m_serviceId
<<
496 "\n SCPDURL: " << m_SCPDURL
<<
497 "\n absSCPDURL: " << m_absSCPDURL
<<
498 "\n controlURL: " << m_controlURL
<<
499 "\n absControlURL: " << m_absControlURL
<<
500 "\n eventSubURL: " << m_eventSubURL
<<
501 "\n absEventSubURL: " << m_absEventSubURL
;
502 AddDebugLogLineN(logUPnP
, msg
);
504 if (m_serviceType
== UPnP::Service::WAN_IP_Connection
||
505 m_serviceType
== UPnP::Service::WAN_PPP_Connection
) {
507 m_serviceType
== UPnP::Service::WAN_PPP_Connection
||
508 m_serviceType
== UPnP::Service::WAN_Common_Interface_Config
||
509 m_serviceType
== UPnP::Service::Layer3_Forwarding
) {
512 //#warning Delete this code on release.
513 if (!upnpControlPoint
.WanServiceDetected()) {
514 // This condition can be used to suspend the parse
517 //#warning Delete this code when m_WanService is no longer used.
518 const_cast<CUPnPControlPoint
&>(upnpControlPoint
).SetWanService(this);
521 msg
<< "WAN Service Detected: '" <<
522 m_serviceType
<< "'.";
523 AddDebugLogLineC(logUPnP
, msg
);
525 const_cast<CUPnPControlPoint
&>(upnpControlPoint
).Subscribe(*this);
527 //#warning Delete this code on release.
530 msg
<< "WAN service detected again: '" <<
532 "'. Will only use the first instance.";
533 AddDebugLogLineC(logUPnP
, msg
);
538 msg
<< "Uninteresting service detected: '" <<
539 m_serviceType
<< "'. Ignoring.";
540 AddDebugLogLineC(logUPnP
, msg
);
545 CUPnPService::~CUPnPService()
550 bool CUPnPService::Execute(
551 const std::string
&ActionName
,
552 const std::vector
<CUPnPArgumentValue
> &ArgValue
) const
554 std::ostringstream msg
;
555 if (m_SCPD
.get() == NULL
) {
556 msg
<< "Service without SCPD Document, cannot execute action '" << ActionName
<<
557 "' for service '" << GetServiceType() << "'.";
558 AddDebugLogLineN(logUPnP
, msg
);
561 std::ostringstream
msgAction("Sending action ");
562 // Check for correct action name
563 ActionList::const_iterator itAction
=
564 m_SCPD
->GetActionList().find(ActionName
);
565 if (itAction
== m_SCPD
->GetActionList().end()) {
566 msg
<< "Invalid action name '" << ActionName
<<
567 "' for service '" << GetServiceType() << "'.";
568 AddDebugLogLineN(logUPnP
, msg
);
571 msgAction
<< ActionName
<< "(";
572 bool firstTime
= true;
573 // Check for correct Argument/Value pairs
574 const CUPnPAction
&action
= *(itAction
->second
);
575 for (unsigned int i
= 0; i
< ArgValue
.size(); ++i
) {
576 ArgumentList::const_iterator itArg
=
577 action
.GetArgumentList().find(ArgValue
[i
].GetArgument());
578 if (itArg
== action
.GetArgumentList().end()) {
579 msg
<< "Invalid argument name '" << ArgValue
[i
].GetArgument() <<
580 "' for action '" << action
.GetName() <<
581 "' for service '" << GetServiceType() << "'.";
582 AddDebugLogLineN(logUPnP
, msg
);
585 const CUPnPArgument
&argument
= *(itArg
->second
);
586 if (tolower(argument
.GetDirection()[0]) != 'i' ||
587 tolower(argument
.GetDirection()[1]) != 'n') {
588 msg
<< "Invalid direction for argument '" <<
589 ArgValue
[i
].GetArgument() <<
590 "' for action '" << action
.GetName() <<
591 "' for service '" << GetServiceType() << "'.";
592 AddDebugLogLineN(logUPnP
, msg
);
595 const std::string relatedStateVariableName
=
596 argument
.GetRelatedStateVariable();
597 if (!relatedStateVariableName
.empty()) {
598 ServiceStateTable::const_iterator itSVT
=
599 m_SCPD
->GetServiceStateTable().
600 find(relatedStateVariableName
);
601 if (itSVT
== m_SCPD
->GetServiceStateTable().end()) {
602 msg
<< "Inconsistent Service State Table, did not find '" <<
603 relatedStateVariableName
<<
604 "' for argument '" << argument
.GetName() <<
605 "' for action '" << action
.GetName() <<
606 "' for service '" << GetServiceType() << "'.";
607 AddDebugLogLineN(logUPnP
, msg
);
610 const CUPnPStateVariable
&stateVariable
= *(itSVT
->second
);
611 if ( !stateVariable
.GetAllowedValueList().empty() &&
612 stateVariable
.GetAllowedValueList().find(ArgValue
[i
].GetValue()) ==
613 stateVariable
.GetAllowedValueList().end()) {
614 msg
<< "Value not allowed '" << ArgValue
[i
].GetValue() <<
615 "' for state variable '" << relatedStateVariableName
<<
616 "' for argument '" << argument
.GetName() <<
617 "' for action '" << action
.GetName() <<
618 "' for service '" << GetServiceType() << "'.";
619 AddDebugLogLineN(logUPnP
, msg
);
629 ArgValue
[i
].GetArgument() <<
631 ArgValue
[i
].GetValue() <<
635 AddDebugLogLineN(logUPnP
, msgAction
);
636 // Everything is ok, make the action
637 IXML_Document
*ActionDoc
= NULL
;
638 if (!ArgValue
.empty()) {
639 for (unsigned int i
= 0; i
< ArgValue
.size(); ++i
) {
640 int ret
= UpnpAddToAction(
642 action
.GetName().c_str(),
643 GetServiceType().c_str(),
644 ArgValue
[i
].GetArgument().c_str(),
645 ArgValue
[i
].GetValue().c_str());
646 if (ret
!= UPNP_E_SUCCESS
) {
647 UPnP::ProcessErrorMessage(
648 "UpnpAddToAction", ret
, NULL
, NULL
);
653 ActionDoc
= UpnpMakeAction(
654 action
.GetName().c_str(),
655 GetServiceType().c_str(),
658 msg
<< "Error: UpnpMakeAction returned NULL.";
659 AddDebugLogLineN(logUPnP
, msg
);
664 // Send the action asynchronously
666 m_UPnPControlPoint
.GetUPnPClientHandle(),
667 GetAbsControlURL().c_str(),
668 GetServiceType().c_str(),
670 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
675 // Send the action synchronously
676 IXML_Document
*RespDoc
= NULL
;
677 int ret
= UpnpSendAction(
678 m_UPnPControlPoint
.GetUPnPClientHandle(),
679 GetAbsControlURL().c_str(),
680 GetServiceType().c_str(),
681 NULL
, ActionDoc
, &RespDoc
);
682 if (ret
!= UPNP_E_SUCCESS
) {
683 UPnP::ProcessErrorMessage(
684 "UpnpSendAction", ret
, NULL
, RespDoc
);
685 IXML::Document::Free(ActionDoc
);
686 IXML::Document::Free(RespDoc
);
689 IXML::Document::Free(ActionDoc
);
691 // Check the response document
692 UPnP::ProcessActionResponse(RespDoc
, action
.GetName());
694 // Free the response document
695 IXML::Document::Free(RespDoc
);
701 const std::string
CUPnPService::GetStateVariable(
702 const std::string
&stateVariableName
) const
704 std::ostringstream msg
;
706 int ret
= UpnpGetServiceVarStatus(
707 m_UPnPControlPoint
.GetUPnPClientHandle(),
708 GetAbsControlURL().c_str(),
709 stateVariableName
.c_str(),
711 if (ret
!= UPNP_E_SUCCESS
) {
712 msg
<< "GetStateVariable(\"" <<
714 "\"): in a call to UpnpGetServiceVarStatus";
715 UPnP::ProcessErrorMessage(
716 msg
.str(), ret
, StVarVal
, NULL
);
717 return stdEmptyString
;
719 msg
<< "GetStateVariable: " <<
724 AddDebugLogLineN(logUPnP
, msg
);
729 CUPnPDevice::CUPnPDevice(
730 const CUPnPControlPoint
&upnpControlPoint
,
731 IXML_Element
*device
,
732 const std::string
&URLBase
)
734 m_DeviceList(upnpControlPoint
, device
, URLBase
),
735 m_ServiceList(upnpControlPoint
, device
, URLBase
),
736 m_deviceType (IXML::Element::GetChildValueByTag(device
, "deviceType")),
737 m_friendlyName (IXML::Element::GetChildValueByTag(device
, "friendlyName")),
738 m_manufacturer (IXML::Element::GetChildValueByTag(device
, "manufacturer")),
739 m_manufacturerURL (IXML::Element::GetChildValueByTag(device
, "manufacturerURL")),
740 m_modelDescription (IXML::Element::GetChildValueByTag(device
, "modelDescription")),
741 m_modelName (IXML::Element::GetChildValueByTag(device
, "modelName")),
742 m_modelNumber (IXML::Element::GetChildValueByTag(device
, "modelNumber")),
743 m_modelURL (IXML::Element::GetChildValueByTag(device
, "modelURL")),
744 m_serialNumber (IXML::Element::GetChildValueByTag(device
, "serialNumber")),
745 m_UDN (IXML::Element::GetChildValueByTag(device
, "UDN")),
746 m_UPC (IXML::Element::GetChildValueByTag(device
, "UPC")),
747 m_presentationURL (IXML::Element::GetChildValueByTag(device
, "presentationURL"))
749 std::ostringstream msg
;
750 int presURLlen
= strlen(URLBase
.c_str()) +
751 strlen(m_presentationURL
.c_str()) + 2;
752 std::vector
<char> vpresURL(presURLlen
);
753 char* presURL
= &vpresURL
[0];
754 int errcode
= UpnpResolveURL(
756 m_presentationURL
.c_str(),
758 if (errcode
!= UPNP_E_SUCCESS
) {
759 msg
<< "Error generating presentationURL from " <<
760 "|" << URLBase
<< "|" <<
761 m_presentationURL
<< "|.";
762 AddDebugLogLineN(logUPnP
, msg
);
764 m_presentationURL
= presURL
;
768 msg
<< "\n Device: " <<
769 "\n friendlyName: " << m_friendlyName
<<
770 "\n deviceType: " << m_deviceType
<<
771 "\n manufacturer: " << m_manufacturer
<<
772 "\n manufacturerURL: " << m_manufacturerURL
<<
773 "\n modelDescription: " << m_modelDescription
<<
774 "\n modelName: " << m_modelName
<<
775 "\n modelNumber: " << m_modelNumber
<<
776 "\n modelURL: " << m_modelURL
<<
777 "\n serialNumber: " << m_serialNumber
<<
778 "\n UDN: " << m_UDN
<<
779 "\n UPC: " << m_UPC
<<
780 "\n presentationURL: " << m_presentationURL
;
781 AddDebugLogLineN(logUPnP
, msg
);
785 CUPnPRootDevice::CUPnPRootDevice(
786 const CUPnPControlPoint
&upnpControlPoint
,
787 IXML_Element
*rootDevice
,
788 const std::string
&OriginalURLBase
,
789 const std::string
&FixedURLBase
,
790 const char *location
,
793 CUPnPDevice(upnpControlPoint
, rootDevice
, FixedURLBase
),
794 m_URLBase(OriginalURLBase
),
795 m_location(location
),
798 std::ostringstream msg
;
800 "\n Root Device: " <<
801 "\n URLBase: " << m_URLBase
<<
802 "\n Fixed URLBase: " << FixedURLBase
<<
803 "\n location: " << m_location
<<
804 "\n expires: " << m_expires
;
805 AddDebugLogLineN(logUPnP
, msg
);
809 CUPnPControlPoint
*CUPnPControlPoint::s_CtrlPoint
= NULL
;
812 CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort
)
814 m_UPnPClientHandle(),
817 m_ActivePortMappingsMap(),
818 m_RootDeviceListMutex(),
819 m_IGWDeviceDetected(false),
824 // Null string at first
825 std::ostringstream msg
;
829 char *ipAddress
= NULL
;
830 unsigned short port
= 0;
831 ret
= UpnpInit(ipAddress
, udpPort
);
832 if (ret
!= UPNP_E_SUCCESS
) {
833 msg
<< "error(UpnpInit): Error code ";
836 port
= UpnpGetServerPort();
837 ipAddress
= UpnpGetServerIpAddress();
838 msg
<< "bound to " << ipAddress
<< ":" <<
840 AddDebugLogLineN(logUPnP
, msg
);
842 ret
= UpnpRegisterClient(
843 static_cast<Upnp_FunPtr
>(&CUPnPControlPoint::Callback
),
845 &m_UPnPClientHandle
);
846 if (ret
!= UPNP_E_SUCCESS
) {
847 msg
<< "error(UpnpRegisterClient): Error registering callback: ";
851 // We could ask for just the right device here. If the root device
852 // contains the device we want, it will respond with the full XML doc,
853 // including the root device and every sub-device it has.
855 // But let's find out what we have in our network by calling UPnP::ROOT_DEVICE.
857 // We should not search twice, because this will produce two
858 // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
860 ret
= UpnpSearchAsync(m_UPnPClientHandle
, 3, UPnP::ROOT_DEVICE
.c_str(), NULL
);
861 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, UPnP::Device::IGW.c_str(), this);
862 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, UPnP::Device::LAN.c_str(), this);
863 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, UPnP::Device::WAN_Connection.c_str(), this);
864 if (ret
!= UPNP_E_SUCCESS
) {
865 msg
<< "error(UpnpSearchAsync): Error sending search request: ";
869 // Wait for the UPnP initialization to complete.
871 // Lock the search timeout mutex
872 m_WaitForSearchTimeoutMutex
.Lock();
874 // Lock it again, so that we block. Unlocking will only happen
875 // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
877 CUPnPMutexLocker
lock(m_WaitForSearchTimeoutMutex
);
884 msg
<< ret
<< ": " << UpnpGetErrorMessage(ret
) << ".";
885 throw CUPnPException(msg
);
889 CUPnPControlPoint::~CUPnPControlPoint()
891 for( RootDeviceMap::iterator it
= m_RootDeviceMap
.begin();
892 it
!= m_RootDeviceMap
.end();
898 UpnpUnRegisterClient(m_UPnPClientHandle
);
903 bool CUPnPControlPoint::AddPortMappings(
904 std::vector
<CUPnPPortMapping
> &upnpPortMapping
)
906 std::ostringstream msg
;
907 if (!WanServiceDetected()) {
908 msg
<< "UPnP Error: "
909 "CUPnPControlPoint::AddPortMapping: "
910 "WAN Service not detected.";
911 AddDebugLogLineC(logUPnP
, msg
);
915 int n
= upnpPortMapping
.size();
918 // Check the number of port mappings before
919 std::istringstream
PortMappingNumberOfEntries(
920 m_WanService
->GetStateVariable(
921 "PortMappingNumberOfEntries"));
922 unsigned long oldNumberOfEntries
;
923 PortMappingNumberOfEntries
>> oldNumberOfEntries
;
925 // Add the enabled port mappings
926 for (int i
= 0; i
< n
; ++i
) {
927 if (upnpPortMapping
[i
].getEnabled() == "1") {
928 // Add the mapping to the control point
929 // active mappings list
930 m_ActivePortMappingsMap
[upnpPortMapping
[i
].getKey()] =
933 // Add the port mapping
934 PrivateAddPortMapping(upnpPortMapping
[i
]);
938 // Test some variables, this is deprecated, might not work
940 m_WanService
->GetStateVariable("ConnectionType");
941 m_WanService
->GetStateVariable("PossibleConnectionTypes");
942 m_WanService
->GetStateVariable("ConnectionStatus");
943 m_WanService
->GetStateVariable("Uptime");
944 m_WanService
->GetStateVariable("LastConnectionError");
945 m_WanService
->GetStateVariable("RSIPAvailable");
946 m_WanService
->GetStateVariable("NATEnabled");
947 m_WanService
->GetStateVariable("ExternalIPAddress");
948 m_WanService
->GetStateVariable("PortMappingNumberOfEntries");
949 m_WanService
->GetStateVariable("PortMappingLeaseDuration");
952 std::vector
<CUPnPArgumentValue
> argval
;
954 m_WanService
->Execute("GetStatusInfo", argval
);
957 // These do not work. Their value must be requested for a
958 // specific port mapping.
959 m_WanService
->GetStateVariable("PortMappingEnabled");
960 m_WanService
->GetStateVariable("RemoteHost");
961 m_WanService
->GetStateVariable("ExternalPort");
962 m_WanService
->GetStateVariable("InternalPort");
963 m_WanService
->GetStateVariable("PortMappingProtocol");
964 m_WanService
->GetStateVariable("InternalClient");
965 m_WanService
->GetStateVariable("PortMappingDescription");
970 msg
<< "CUPnPControlPoint::AddPortMappings: "
971 "m_ActivePortMappingsMap.size() == " <<
972 m_ActivePortMappingsMap
.size();
973 AddDebugLogLineN(logUPnP
, msg
);
975 // Not very good, must find a better test
976 PortMappingNumberOfEntries
.str(
977 m_WanService
->GetStateVariable(
978 "PortMappingNumberOfEntries"));
979 unsigned long newNumberOfEntries
;
980 PortMappingNumberOfEntries
>> newNumberOfEntries
;
981 ok
= newNumberOfEntries
- oldNumberOfEntries
== 4;
987 void CUPnPControlPoint::RefreshPortMappings()
989 for ( PortMappingMap::iterator it
= m_ActivePortMappingsMap
.begin();
990 it
!= m_ActivePortMappingsMap
.end();
992 PrivateAddPortMapping(it
->second
);
996 m_WanService
->GetStateVariable("PortMappingNumberOfEntries");
1000 bool CUPnPControlPoint::PrivateAddPortMapping(
1001 CUPnPPortMapping
&upnpPortMapping
)
1003 // Get an IP address. The UPnP server one must do.
1004 std::string
ipAddress(UpnpGetServerIpAddress());
1006 // Start building the action
1007 std::string
actionName("AddPortMapping");
1008 std::vector
<CUPnPArgumentValue
> argval(8);
1010 // Action parameters
1011 argval
[0].SetArgument("NewRemoteHost");
1012 argval
[0].SetValue("");
1013 argval
[1].SetArgument("NewExternalPort");
1014 argval
[1].SetValue(upnpPortMapping
.getPort());
1015 argval
[2].SetArgument("NewProtocol");
1016 argval
[2].SetValue(upnpPortMapping
.getProtocol());
1017 argval
[3].SetArgument("NewInternalPort");
1018 argval
[3].SetValue(upnpPortMapping
.getPort());
1019 argval
[4].SetArgument("NewInternalClient");
1020 argval
[4].SetValue(ipAddress
);
1021 argval
[5].SetArgument("NewEnabled");
1022 argval
[5].SetValue("1");
1023 argval
[6].SetArgument("NewPortMappingDescription");
1024 argval
[6].SetValue(upnpPortMapping
.getDescription());
1025 argval
[7].SetArgument("NewLeaseDuration");
1026 argval
[7].SetValue("0");
1030 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1031 it
!= m_ServiceMap
.end(); ++it
) {
1032 ret
&= it
->second
->Execute(actionName
, argval
);
1039 bool CUPnPControlPoint::DeletePortMappings(
1040 std::vector
<CUPnPPortMapping
> &upnpPortMapping
)
1042 std::ostringstream msg
;
1043 if (!WanServiceDetected()) {
1044 msg
<< "UPnP Error: "
1045 "CUPnPControlPoint::DeletePortMapping: "
1046 "WAN Service not detected.";
1047 AddDebugLogLineC(logUPnP
, msg
);
1051 int n
= upnpPortMapping
.size();
1054 // Check the number of port mappings before
1055 std::istringstream
PortMappingNumberOfEntries(
1056 m_WanService
->GetStateVariable(
1057 "PortMappingNumberOfEntries"));
1058 unsigned long oldNumberOfEntries
;
1059 PortMappingNumberOfEntries
>> oldNumberOfEntries
;
1061 // Delete the enabled port mappings
1062 for (int i
= 0; i
< n
; ++i
) {
1063 if (upnpPortMapping
[i
].getEnabled() == "1") {
1064 // Delete the mapping from the control point
1065 // active mappings list
1066 PortMappingMap::iterator it
=
1067 m_ActivePortMappingsMap
.find(
1068 upnpPortMapping
[i
].getKey());
1069 if (it
!= m_ActivePortMappingsMap
.end()) {
1070 m_ActivePortMappingsMap
.erase(it
);
1072 msg
<< "UPnP Error: "
1073 "CUPnPControlPoint::DeletePortMapping: "
1074 "Mapping was not found in the active "
1076 AddDebugLogLineC(logUPnP
, msg
);
1079 // Delete the port mapping
1080 PrivateDeletePortMapping(upnpPortMapping
[i
]);
1086 msg
<< "CUPnPControlPoint::DeletePortMappings: "
1087 "m_ActivePortMappingsMap.size() == " <<
1088 m_ActivePortMappingsMap
.size();
1089 AddDebugLogLineN(logUPnP
, msg
);
1091 // Not very good, must find a better test
1092 PortMappingNumberOfEntries
.str(
1093 m_WanService
->GetStateVariable(
1094 "PortMappingNumberOfEntries"));
1095 unsigned long newNumberOfEntries
;
1096 PortMappingNumberOfEntries
>> newNumberOfEntries
;
1097 ok
= oldNumberOfEntries
- newNumberOfEntries
== 4;
1103 bool CUPnPControlPoint::PrivateDeletePortMapping(
1104 CUPnPPortMapping
&upnpPortMapping
)
1106 // Start building the action
1107 std::string
actionName("DeletePortMapping");
1108 std::vector
<CUPnPArgumentValue
> argval(3);
1110 // Action parameters
1111 argval
[0].SetArgument("NewRemoteHost");
1112 argval
[0].SetValue("");
1113 argval
[1].SetArgument("NewExternalPort");
1114 argval
[1].SetValue(upnpPortMapping
.getPort());
1115 argval
[2].SetArgument("NewProtocol");
1116 argval
[2].SetValue(upnpPortMapping
.getProtocol());
1120 for (ServiceMap::iterator it
= m_ServiceMap
.begin();
1121 it
!= m_ServiceMap
.end(); ++it
) {
1122 ret
&= it
->second
->Execute(actionName
, argval
);
1129 // This function is static
1130 int CUPnPControlPoint::Callback(Upnp_EventType EventType
, void *Event
, void * /*Cookie*/)
1132 std::ostringstream msg
;
1133 std::ostringstream msg2
;
1134 // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
1135 // happen with a wrong cookie and... boom!
1136 // CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
1137 CUPnPControlPoint
*upnpCP
= CUPnPControlPoint::s_CtrlPoint
;
1139 //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
1140 switch (EventType
) {
1141 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
:
1142 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
1143 msg
<< "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): ";
1144 msg2
<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
1146 case UPNP_DISCOVERY_SEARCH_RESULT
: {
1147 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n");
1148 msg
<< "error(UPNP_DISCOVERY_SEARCH_RESULT): ";
1149 msg2
<< "UPNP_DISCOVERY_SEARCH_RESULT: ";
1152 struct Upnp_Discovery
*d_event
= (struct Upnp_Discovery
*)Event
;
1153 IXML_Document
*doc
= NULL
;
1155 if (d_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1156 msg
<< UpnpGetErrorMessage(d_event
->ErrCode
) << ".";
1157 AddDebugLogLineC(logUPnP
, msg
);
1159 // Get the XML tree device description in doc
1160 ret
= UpnpDownloadXmlDoc(d_event
->Location
, &doc
);
1161 if (ret
!= UPNP_E_SUCCESS
) {
1162 msg
<< "Error retrieving device description from " <<
1163 d_event
->Location
<< ": " <<
1164 UpnpGetErrorMessage(ret
) <<
1166 AddDebugLogLineC(logUPnP
, msg
);
1168 msg2
<< "Retrieving device description from " <<
1169 d_event
->Location
<< ".";
1170 AddDebugLogLineN(logUPnP
, msg2
);
1173 // Get the root node
1174 IXML_Element
*root
= IXML::Document::GetRootElement(doc
);
1175 // Extract the URLBase
1176 const std::string urlBase
= IXML::Element::GetChildValueByTag(root
, "URLBase");
1177 // Get the root device
1178 IXML_Element
*rootDevice
= IXML::Element::GetFirstChildByTag(root
, "device");
1179 // Extract the deviceType
1180 std::string
devType(IXML::Element::GetChildValueByTag(rootDevice
, "deviceType"));
1181 // Only add device if it is an InternetGatewayDevice
1182 if (stdStringIsEqualCI(devType
, UPnP::Device::IGW
)) {
1183 // This condition can be used to auto-detect
1184 // the UPnP device we are interested in.
1185 // Obs.: Don't block the entry here on this
1186 // condition! There may be more than one device,
1187 // and the first that enters may not be the one
1188 // we are interested in!
1189 upnpCP
->SetIGWDeviceDetected(true);
1190 // Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
1191 // we don't want to spam our logs.
1192 if (EventType
!= UPNP_DISCOVERY_ADVERTISEMENT_ALIVE
) {
1193 msg
.str("Internet Gateway Device Detected.");
1194 AddDebugLogLineC(logUPnP
, msg
);
1196 // Add the root device to our list
1197 upnpCP
->AddRootDevice(rootDevice
, urlBase
,
1198 d_event
->Location
, d_event
->Expires
);
1200 // Free the XML doc tree
1201 IXML::Document::Free(doc
);
1205 case UPNP_DISCOVERY_SEARCH_TIMEOUT
: {
1206 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1208 msg
<< "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1209 AddDebugLogLineN(logUPnP
, msg
);
1211 // Unlock the search timeout mutex
1212 upnpCP
->m_WaitForSearchTimeoutMutex
.Unlock();
1216 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE
: {
1217 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
1218 // UPnP Device Removed
1219 struct Upnp_Discovery
*dab_event
= (struct Upnp_Discovery
*)Event
;
1220 if (dab_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1221 msg
<< "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
1222 UpnpGetErrorMessage(dab_event
->ErrCode
) <<
1224 AddDebugLogLineC(logUPnP
, msg
);
1226 std::string devType
= dab_event
->DeviceType
;
1227 // Check for an InternetGatewayDevice and removes it from the list
1228 std::transform(devType
.begin(), devType
.end(), devType
.begin(), tolower
);
1229 if (stdStringIsEqualCI(devType
, UPnP::Device::IGW
)) {
1230 upnpCP
->RemoveRootDevice(dab_event
->DeviceId
);
1234 case UPNP_EVENT_RECEIVED
: {
1235 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1237 struct Upnp_Event
*e_event
= (struct Upnp_Event
*)Event
;
1238 const std::string Sid
= e_event
->Sid
;
1240 upnpCP
->OnEventReceived(Sid
, e_event
->EventKey
, e_event
->ChangedVariables
);
1243 case UPNP_EVENT_SUBSCRIBE_COMPLETE
:
1244 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
1245 msg
<< "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): ";
1246 goto upnpEventRenewalComplete
;
1247 case UPNP_EVENT_UNSUBSCRIBE_COMPLETE
:
1248 //fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
1249 msg
<< "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): ";
1250 goto upnpEventRenewalComplete
;
1251 case UPNP_EVENT_RENEWAL_COMPLETE
: {
1252 //fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n");
1253 msg
<< "error(UPNP_EVENT_RENEWAL_COMPLETE): ";
1254 upnpEventRenewalComplete
:
1255 struct Upnp_Event_Subscribe
*es_event
=
1256 (struct Upnp_Event_Subscribe
*)Event
;
1257 if (es_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1258 msg
<< "Error in Event Subscribe Callback";
1259 UPnP::ProcessErrorMessage(
1260 msg
.str(), es_event
->ErrCode
, NULL
, NULL
);
1263 TvCtrlPointHandleSubscribeUpdate(
1264 GET_UPNP_STRING(es_event
->PublisherUrl
),
1266 es_event
->TimeOut
);
1273 case UPNP_EVENT_AUTORENEWAL_FAILED
:
1274 //fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n");
1275 msg
<< "error(UPNP_EVENT_AUTORENEWAL_FAILED): ";
1276 msg2
<< "UPNP_EVENT_AUTORENEWAL_FAILED: ";
1277 goto upnpEventSubscriptionExpired
;
1278 case UPNP_EVENT_SUBSCRIPTION_EXPIRED
: {
1279 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
1280 msg
<< "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): ";
1281 msg2
<< "UPNP_EVENT_SUBSCRIPTION_EXPIRED: ";
1282 upnpEventSubscriptionExpired
:
1283 struct Upnp_Event_Subscribe
*es_event
=
1284 (struct Upnp_Event_Subscribe
*)Event
;
1286 memset(newSID
, 0, sizeof(Upnp_SID
));
1288 int ret
= UpnpSubscribe(
1289 upnpCP
->m_UPnPClientHandle
,
1290 GET_UPNP_STRING(es_event
->PublisherUrl
),
1293 if (ret
!= UPNP_E_SUCCESS
) {
1294 msg
<< "Error Subscribing to EventURL";
1295 UPnP::ProcessErrorMessage(
1296 msg
.str(), es_event
->ErrCode
, NULL
, NULL
);
1298 ServiceMap::iterator it
=
1299 upnpCP
->m_ServiceMap
.find(GET_UPNP_STRING(es_event
->PublisherUrl
));
1300 if (it
!= upnpCP
->m_ServiceMap
.end()) {
1301 CUPnPService
&service
= *(it
->second
);
1302 service
.SetTimeout(TimeOut
);
1303 service
.SetSID(newSID
);
1304 msg2
<< "Re-subscribed to EventURL '" <<
1305 GET_UPNP_STRING(es_event
->PublisherUrl
) <<
1306 "' with SID == '" <<
1308 AddDebugLogLineC(logUPnP
, msg2
);
1309 // In principle, we should test to see if the
1310 // service is the same. But here we only have one
1312 upnpCP
->RefreshPortMappings();
1314 msg
<< "Error: did not find service " <<
1315 newSID
<< " in the service map.";
1316 AddDebugLogLineC(logUPnP
, msg
);
1321 case UPNP_CONTROL_ACTION_COMPLETE
: {
1322 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n");
1323 // This is here if we choose to do this asynchronously
1324 struct Upnp_Action_Complete
*a_event
=
1325 (struct Upnp_Action_Complete
*)Event
;
1326 if (a_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1327 UPnP::ProcessErrorMessage(
1328 "UpnpSendActionAsync",
1329 a_event
->ErrCode
, NULL
,
1330 a_event
->ActionResult
);
1332 // Check the response document
1333 UPnP::ProcessActionResponse(
1334 a_event
->ActionResult
,
1335 "<UpnpSendActionAsync>");
1337 /* No need for any processing here, just print out results.
1338 * Service state table updates are handled by events.
1342 case UPNP_CONTROL_GET_VAR_COMPLETE
: {
1343 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n");
1344 msg
<< "error(UPNP_CONTROL_GET_VAR_COMPLETE): ";
1345 struct Upnp_State_Var_Complete
*sv_event
=
1346 (struct Upnp_State_Var_Complete
*)Event
;
1347 if (sv_event
->ErrCode
!= UPNP_E_SUCCESS
) {
1348 msg
<< "m_UpnpGetServiceVarStatusAsync";
1349 UPnP::ProcessErrorMessage(
1350 msg
.str(), sv_event
->ErrCode
, NULL
, NULL
);
1353 // Warning: The use of UpnpGetServiceVarStatus and
1354 // UpnpGetServiceVarStatusAsync is deprecated by the
1356 TvCtrlPointHandleGetVar(
1358 sv_event
->StateVarName
,
1359 sv_event
->CurrentVal
);
1364 // ignore these cases, since this is not a device
1365 case UPNP_CONTROL_GET_VAR_REQUEST
:
1366 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n");
1367 msg
<< "error(UPNP_CONTROL_GET_VAR_REQUEST): ";
1368 goto eventSubscriptionRequest
;
1369 case UPNP_CONTROL_ACTION_REQUEST
:
1370 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n");
1371 msg
<< "error(UPNP_CONTROL_ACTION_REQUEST): ";
1372 goto eventSubscriptionRequest
;
1373 case UPNP_EVENT_SUBSCRIPTION_REQUEST
:
1374 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
1375 msg
<< "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): ";
1376 eventSubscriptionRequest
:
1377 msg
<< "This is not a UPnP Device, this is a UPnP Control Point, event ignored.";
1378 AddDebugLogLineC(logUPnP
, msg
);
1381 // Humm, this is not good, we forgot to handle something...
1383 "Callback: default... Unknown event:'%d', not good.\n",
1385 msg
<< "error(UPnP::Callback): Event not handled:'" <<
1387 fprintf(stderr
, "%s\n", msg
.str().c_str());
1388 AddDebugLogLineC(logUPnP
, msg
);
1389 // Better not throw in the callback. Who would catch it?
1390 //throw CUPnPException(msg);
1398 void CUPnPControlPoint::OnEventReceived(
1399 const std::string
&Sid
,
1401 IXML_Document
*ChangedVariablesDoc
)
1403 std::ostringstream msg
;
1404 msg
<< "UPNP_EVENT_RECEIVED:" <<
1405 "\n SID: " << Sid
<<
1406 "\n Key: " << EventKey
<<
1407 "\n Property list:";
1408 IXML_Element
*root
= IXML::Document::GetRootElement(ChangedVariablesDoc
);
1409 IXML_Element
*child
= IXML::Element::GetFirstChild(root
);
1412 IXML_Element
*child2
= IXML::Element::GetFirstChild(child
);
1413 const DOMString childTag
= IXML::Element::GetTag(child2
);
1414 std::string childValue
= IXML::Element::GetTextValue(child2
);
1418 child
= IXML::Element::GetNextSibling(child
);
1421 msg
<< "\n Empty property list.";
1423 AddDebugLogLineC(logUPnP
, msg
);
1427 void CUPnPControlPoint::AddRootDevice(
1428 IXML_Element
*rootDevice
, const std::string
&urlBase
,
1429 const char *location
, int expires
)
1431 // Lock the Root Device List
1432 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1434 // Root node's URLBase
1435 std::string
OriginalURLBase(urlBase
);
1436 std::string
FixedURLBase(OriginalURLBase
.empty() ?
1440 // Get the UDN (Unique Device Name)
1441 std::string
UDN(IXML::Element::GetChildValueByTag(rootDevice
, "UDN"));
1442 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1443 bool alreadyAdded
= it
!= m_RootDeviceMap
.end();
1445 // Just set the expires field
1446 it
->second
->SetExpires(expires
);
1448 // Add a new root device to the root device list
1449 CUPnPRootDevice
*upnpRootDevice
= new CUPnPRootDevice(
1451 OriginalURLBase
, FixedURLBase
,
1453 m_RootDeviceMap
[upnpRootDevice
->GetUDN()] = upnpRootDevice
;
1458 void CUPnPControlPoint::RemoveRootDevice(const char *udn
)
1460 // Lock the Root Device List
1461 CUPnPMutexLocker
lock(m_RootDeviceListMutex
);
1464 std::string
UDN(udn
);
1465 RootDeviceMap::iterator it
= m_RootDeviceMap
.find(UDN
);
1466 if (it
!= m_RootDeviceMap
.end()) {
1468 m_RootDeviceMap
.erase(UDN
);
1473 void CUPnPControlPoint::Subscribe(CUPnPService
&service
)
1475 std::ostringstream msg
;
1477 IXML_Document
*scpdDoc
= NULL
;
1478 int errcode
= UpnpDownloadXmlDoc(
1479 service
.GetAbsSCPDURL().c_str(), &scpdDoc
);
1480 if (errcode
== UPNP_E_SUCCESS
) {
1481 // Get the root node of this service (the SCPD Document)
1482 IXML_Element
*scpdRoot
= IXML::Document::GetRootElement(scpdDoc
);
1483 CUPnPSCPD
*scpd
= new CUPnPSCPD(*this, scpdRoot
, service
.GetAbsSCPDURL());
1484 service
.SetSCPD(scpd
);
1485 IXML::Document::Free(scpdDoc
);
1486 m_ServiceMap
[service
.GetAbsEventSubURL()] = &service
;
1487 msg
<< "Successfully retrieved SCPD Document for service " <<
1488 service
.GetServiceType() << ", absEventSubURL: " <<
1489 service
.GetAbsEventSubURL() << ".";
1490 AddDebugLogLineC(logUPnP
, msg
);
1493 // Now try to subscribe to this service. If the subscription
1494 // is not successfull, we will not be notified about events,
1495 // but it may be possible to use the service anyway.
1496 errcode
= UpnpSubscribe(m_UPnPClientHandle
,
1497 service
.GetAbsEventSubURL().c_str(),
1498 service
.GetTimeoutAddr(),
1500 if (errcode
== UPNP_E_SUCCESS
) {
1501 msg
<< "Successfully subscribed to service " <<
1502 service
.GetServiceType() << ", absEventSubURL: " <<
1503 service
.GetAbsEventSubURL() << ".";
1504 AddDebugLogLineC(logUPnP
, msg
);
1506 msg
<< "Error subscribing to service " <<
1507 service
.GetServiceType() << ", absEventSubURL: " <<
1508 service
.GetAbsEventSubURL() << ", error: " <<
1509 UpnpGetErrorMessage(errcode
) << ".";
1513 msg
<< "Error getting SCPD Document from " <<
1514 service
.GetAbsSCPDURL() << ".";
1515 AddDebugLogLineC(logUPnP
, msg
);
1522 AddDebugLogLineC(logUPnP
, msg
);
1526 void CUPnPControlPoint::Unsubscribe(CUPnPService
&service
)
1528 ServiceMap::iterator it
= m_ServiceMap
.find(service
.GetAbsEventSubURL());
1529 if (it
!= m_ServiceMap
.end()) {
1530 m_ServiceMap
.erase(it
);
1531 UpnpUnSubscribe(m_UPnPClientHandle
, service
.GetSID());
1535 #endif /* ENABLE_UPNP */