Fix a memory leak in our UPnP handler code
[amule.git] / src / UPnPBase.cpp
blob01a7c3a05ea41f096c1c2b6d9f5226ae0b11a0e1
1 //
2 // This file is part of the aMule Project.
3 //
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 )
6 //
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
9 // respective authors.
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 #ifdef HAVE_CONFIG_H
27 # include "config.h" // Needed for ENABLE_UPNP
28 #endif
30 #ifdef ENABLE_UPNP
32 // check for broken Debian-hacked libUPnP
33 #include <upnp.h>
34 #ifdef STRING_H // defined in UpnpString.h Yes, I would have liked UPNPSTRING_H much better.
35 #define BROKEN_DEBIAN_LIBUPNP
36 #endif
38 #include "UPnPBase.h"
40 #include <algorithm> // For transform()
42 #ifdef BROKEN_DEBIAN_LIBUPNP
43 #define GET_UPNP_STRING(a) UpnpString_get_String(a)
44 #else
45 #define GET_UPNP_STRING(a) (a)
46 #endif
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";
63 /**
64 * Case insensitive std::string comparison
66 bool stdStringIsEqualCI(const std::string &s1, const std::string &s2)
68 std::string ns1(s1);
69 std::string ns2(s2);
70 std::transform(ns1.begin(), ns1.end(), ns1.begin(), tolower);
71 std::transform(ns2.begin(), ns2.end(), ns2.begin(), tolower);
72 return ns1 == ns2;
76 CUPnPPortMapping::CUPnPPortMapping(
77 int port,
78 const std::string &protocol,
79 bool enabled,
80 const std::string &description)
82 m_port(),
83 m_protocol(protocol),
84 m_enabled(enabled ? "1" : "0"),
85 m_description(description),
86 m_key()
88 std::ostringstream oss;
89 oss << port;
90 m_port = oss.str();
91 m_key = m_protocol + m_port;
94 namespace UPnP {
96 static const std::string ROOT_DEVICE("upnp:rootdevice");
98 namespace Device {
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");
105 namespace Service {
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,
114 int errorCode,
115 const DOMString errorString,
116 IXML_Document *doc)
118 std::ostringstream msg;
119 if (errorString == NULL || *errorString == 0) {
120 errorString = "Not available";
122 if (errorCode > 0) {
123 msg << "Error: " <<
124 messsage <<
125 ": Error code :'";
126 if (doc) {
127 CUPnPError e(doc);
128 msg << e.getErrorCode() <<
129 "', Error description :'" <<
130 e.getErrorDescription() <<
131 "'.";
132 } else {
133 msg << errorCode <<
134 "', Error description :'" <<
135 errorString <<
136 "'.";
138 AddDebugLogLineN(logUPnP, msg);
139 } else {
140 msg << "Error: " <<
141 messsage <<
142 ": UPnP SDK error: " <<
143 UpnpGetErrorMessage(errorCode) <<
144 " (" << errorCode << ").";
145 AddDebugLogLineN(logUPnP, msg);
148 return msg.str();
152 static void ProcessActionResponse(
153 IXML_Document *RespDoc,
154 const std::string &actionName)
156 std::ostringstream msg;
157 msg << "Response: ";
158 IXML_Element *root = IXML::Document::GetRootElement(RespDoc);
159 IXML_Element *child = IXML::Element::GetFirstChild(root);
160 if (child) {
161 while (child) {
162 const DOMString childTag = IXML::Element::GetTag(child);
163 std::string childValue = IXML::Element::GetTextValue(child);
164 msg << "\n " <<
165 childTag << "='" <<
166 childValue << "'";
167 child = IXML::Element::GetNextSibling(child);
169 } else {
170 msg << "\n Empty response for action '" <<
171 actionName << "'.";
173 AddDebugLogLineN(logUPnP, msg);
176 } /* namespace UPnP */
179 namespace IXML {
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);
200 namespace Element {
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)
235 if (!element) {
236 return stdEmptyString;
238 IXML_Node *text = ixmlNode_getFirstChild(&element->n);
239 const DOMString s = ixmlNode_getNodeValue(text);
240 std::string ret;
241 if (s) {
242 ret = s;
245 return ret;
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
260 * NULL if not found.
262 IXML_Element *GetFirstChildByTag(IXML_Element *element, const DOMString tag)
264 if (!element || !tag) {
265 return NULL;
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) {
286 return NULL;
289 IXML_Node *child = &element->n;
290 const DOMString childTag = NULL;
291 do {
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);
305 std::string ret;
306 if (s) {
307 ret = s;
309 ixmlNamedNodeMap_free(NamedNodeMap);
311 return ret;
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,
400 IXML_Element *scpd,
401 const std::string &SCPDURL)
403 m_ActionList(upnpControlPoint, scpd, SCPDURL),
404 m_ServiceStateTable(upnpControlPoint, scpd, SCPDURL),
405 m_SCPDURL(SCPDURL)
410 CUPnPArgumentValue::CUPnPArgumentValue()
412 m_argument(),
413 m_value()
418 CUPnPArgumentValue::CUPnPArgumentValue(
419 const std::string &argument, const std::string &value)
421 m_argument(argument),
422 m_value(value)
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")),
438 m_timeout(1801),
439 m_SCPD(NULL)
441 std::ostringstream msg;
442 int errcode;
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(
449 URLBase.c_str(),
450 m_SCPDURL.c_str(),
451 scpdURL);
452 if( errcode != UPNP_E_SUCCESS ) {
453 msg << "Error generating scpdURL from " <<
454 "|" << URLBase << "|" <<
455 m_SCPDURL << "|.";
456 AddDebugLogLineN(logUPnP, msg);
457 } else {
458 m_absSCPDURL = scpdURL;
461 std::vector<char> vcontrolURL(
462 URLBase.length() + m_controlURL.length() + 1);
463 char *controlURL = &vcontrolURL[0];
464 errcode = UpnpResolveURL(
465 URLBase.c_str(),
466 m_controlURL.c_str(),
467 controlURL);
468 if( errcode != UPNP_E_SUCCESS ) {
469 msg << "Error generating controlURL from " <<
470 "|" << URLBase << "|" <<
471 m_controlURL << "|.";
472 AddDebugLogLineN(logUPnP, msg);
473 } else {
474 m_absControlURL = controlURL;
477 std::vector<char> veventURL(
478 URLBase.length() + m_eventSubURL.length() + 1);
479 char *eventURL = &veventURL[0];
480 errcode = UpnpResolveURL(
481 URLBase.c_str(),
482 m_eventSubURL.c_str(),
483 eventURL);
484 if( errcode != UPNP_E_SUCCESS ) {
485 msg << "Error generating eventURL from " <<
486 "|" << URLBase << "|" <<
487 m_eventSubURL << "|.";
488 AddDebugLogLineN(logUPnP, msg);
489 } else {
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) {
506 #if 0
507 m_serviceType == UPnP::Service::WAN_PPP_Connection ||
508 m_serviceType == UPnP::Service::WAN_Common_Interface_Config ||
509 m_serviceType == UPnP::Service::Layer3_Forwarding) {
510 #endif
511 #if 0
512 //#warning Delete this code on release.
513 if (!upnpControlPoint.WanServiceDetected()) {
514 // This condition can be used to suspend the parse
515 // of the XML tree.
516 #endif
517 //#warning Delete this code when m_WanService is no longer used.
518 const_cast<CUPnPControlPoint &>(upnpControlPoint).SetWanService(this);
519 // Log it
520 msg.str("");
521 msg << "WAN Service Detected: '" <<
522 m_serviceType << "'.";
523 AddDebugLogLineC(logUPnP, msg);
524 // Subscribe
525 const_cast<CUPnPControlPoint &>(upnpControlPoint).Subscribe(*this);
526 #if 0
527 //#warning Delete this code on release.
528 } else {
529 msg.str("");
530 msg << "WAN service detected again: '" <<
531 m_serviceType <<
532 "'. Will only use the first instance.";
533 AddDebugLogLineC(logUPnP, msg);
535 #endif
536 } else {
537 msg.str("");
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);
559 return false;
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);
569 return false;
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);
583 return false;
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);
593 return false;
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);
608 return false;
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);
620 return false;
623 if (firstTime) {
624 firstTime = false;
625 } else {
626 msgAction << ", ";
628 msgAction <<
629 ArgValue[i].GetArgument() <<
630 "='" <<
631 ArgValue[i].GetValue() <<
632 "'";
634 msgAction << ")";
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(
641 &ActionDoc,
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);
649 return false;
652 } else {
653 ActionDoc = UpnpMakeAction(
654 action.GetName().c_str(),
655 GetServiceType().c_str(),
656 0, NULL);
657 if (!ActionDoc) {
658 msg << "Error: UpnpMakeAction returned NULL.";
659 AddDebugLogLineN(logUPnP, msg);
660 return false;
663 #if 0
664 // Send the action asynchronously
665 UpnpSendActionAsync(
666 m_UPnPControlPoint.GetUPnPClientHandle(),
667 GetAbsControlURL().c_str(),
668 GetServiceType().c_str(),
669 NULL, ActionDoc,
670 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
671 NULL);
672 return true;
673 #endif
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);
687 return false;
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);
697 return true;
701 const std::string CUPnPService::GetStateVariable(
702 const std::string &stateVariableName) const
704 std::ostringstream msg;
705 DOMString StVarVal;
706 int ret = UpnpGetServiceVarStatus(
707 m_UPnPControlPoint.GetUPnPClientHandle(),
708 GetAbsControlURL().c_str(),
709 stateVariableName.c_str(),
710 &StVarVal);
711 if (ret != UPNP_E_SUCCESS) {
712 msg << "GetStateVariable(\"" <<
713 stateVariableName <<
714 "\"): in a call to UpnpGetServiceVarStatus";
715 UPnP::ProcessErrorMessage(
716 msg.str(), ret, StVarVal, NULL);
717 return stdEmptyString;
719 msg << "GetStateVariable: " <<
720 stateVariableName <<
721 "='" <<
722 StVarVal <<
723 "'.";
724 AddDebugLogLineN(logUPnP, msg);
725 return StVarVal;
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(
755 URLBase.c_str(),
756 m_presentationURL.c_str(),
757 presURL);
758 if (errcode != UPNP_E_SUCCESS) {
759 msg << "Error generating presentationURL from " <<
760 "|" << URLBase << "|" <<
761 m_presentationURL << "|.";
762 AddDebugLogLineN(logUPnP, msg);
763 } else {
764 m_presentationURL = presURL;
767 msg.str("");
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,
791 int expires)
793 CUPnPDevice(upnpControlPoint, rootDevice, FixedURLBase),
794 m_URLBase(OriginalURLBase),
795 m_location(location),
796 m_expires(expires)
798 std::ostringstream msg;
799 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(),
815 m_RootDeviceMap(),
816 m_ServiceMap(),
817 m_ActivePortMappingsMap(),
818 m_RootDeviceListMutex(),
819 m_IGWDeviceDetected(false),
820 m_WanService(NULL)
822 // Pointer to self
823 s_CtrlPoint = this;
824 // Null string at first
825 std::ostringstream msg;
827 // Start UPnP
828 int ret;
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 ";
834 goto error;
836 port = UpnpGetServerPort();
837 ipAddress = UpnpGetServerIpAddress();
838 msg << "bound to " << ipAddress << ":" <<
839 port << ".";
840 AddDebugLogLineN(logUPnP, msg);
841 msg.str("");
842 ret = UpnpRegisterClient(
843 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
844 &m_UPnPClientHandle,
845 &m_UPnPClientHandle);
846 if (ret != UPNP_E_SUCCESS) {
847 msg << "error(UpnpRegisterClient): Error registering callback: ";
848 goto error;
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
859 // on the mutex.
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: ";
866 goto error;
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
876 // callback.
877 CUPnPMutexLocker lock(m_WaitForSearchTimeoutMutex);
879 return;
881 // Error processing
882 error:
883 UpnpFinish();
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();
893 ++it) {
894 delete it->second;
896 // Remove all first
897 // RemoveAll();
898 UpnpUnRegisterClient(m_UPnPClientHandle);
899 UpnpFinish();
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);
912 return false;
915 int n = upnpPortMapping.size();
916 bool ok = false;
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()] =
931 upnpPortMapping[i];
933 // Add the port mapping
934 PrivateAddPortMapping(upnpPortMapping[i]);
938 // Test some variables, this is deprecated, might not work
939 // with some routers
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");
951 // Just for testing
952 std::vector<CUPnPArgumentValue> argval;
953 argval.resize(0);
954 m_WanService->Execute("GetStatusInfo", argval);
956 #if 0
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");
966 #endif
968 // Debug only
969 msg.str("");
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;
983 return ok;
987 void CUPnPControlPoint::RefreshPortMappings()
989 for ( PortMappingMap::iterator it = m_ActivePortMappingsMap.begin();
990 it != m_ActivePortMappingsMap.end();
991 ++it) {
992 PrivateAddPortMapping(it->second);
995 // For testing
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");
1028 // Execute
1029 bool ret = true;
1030 for (ServiceMap::iterator it = m_ServiceMap.begin();
1031 it != m_ServiceMap.end(); ++it) {
1032 ret &= it->second->Execute(actionName, argval);
1035 return ret;
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);
1048 return false;
1051 int n = upnpPortMapping.size();
1052 bool ok = false;
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);
1071 } else {
1072 msg << "UPnP Error: "
1073 "CUPnPControlPoint::DeletePortMapping: "
1074 "Mapping was not found in the active "
1075 "mapping map.";
1076 AddDebugLogLineC(logUPnP, msg);
1079 // Delete the port mapping
1080 PrivateDeletePortMapping(upnpPortMapping[i]);
1084 // Debug only
1085 msg.str("");
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;
1099 return ok;
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());
1118 // Execute
1119 bool ret = true;
1120 for (ServiceMap::iterator it = m_ServiceMap.begin();
1121 it != m_ServiceMap.end(); ++it) {
1122 ret &= it->second->Execute(actionName, argval);
1125 return ret;
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: ";
1145 goto upnpDiscovery;
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: ";
1150 // UPnP Discovery
1151 upnpDiscovery:
1152 struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
1153 IXML_Document *doc = NULL;
1154 int ret;
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) <<
1165 "(" << ret << ").";
1166 AddDebugLogLineC(logUPnP, msg);
1167 } else {
1168 msg2 << "Retrieving device description from " <<
1169 d_event->Location << ".";
1170 AddDebugLogLineN(logUPnP, msg2);
1172 if (doc) {
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);
1203 break;
1205 case UPNP_DISCOVERY_SEARCH_TIMEOUT: {
1206 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1207 // Search timeout
1208 msg << "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1209 AddDebugLogLineN(logUPnP, msg);
1211 // Unlock the search timeout mutex
1212 upnpCP->m_WaitForSearchTimeoutMutex.Unlock();
1214 break;
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) <<
1223 ".";
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);
1232 break;
1234 case UPNP_EVENT_RECEIVED: {
1235 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1236 // Event reveived
1237 struct Upnp_Event *e_event = (struct Upnp_Event *)Event;
1238 const std::string Sid = e_event->Sid;
1239 // Parses the event
1240 upnpCP->OnEventReceived(Sid, e_event->EventKey, e_event->ChangedVariables);
1241 break;
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);
1261 } else {
1262 #if 0
1263 TvCtrlPointHandleSubscribeUpdate(
1264 GET_UPNP_STRING(es_event->PublisherUrl),
1265 es_event->Sid,
1266 es_event->TimeOut );
1267 #endif
1270 break;
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;
1285 Upnp_SID newSID;
1286 memset(newSID, 0, sizeof(Upnp_SID));
1287 int TimeOut = 1801;
1288 int ret = UpnpSubscribe(
1289 upnpCP->m_UPnPClientHandle,
1290 GET_UPNP_STRING(es_event->PublisherUrl),
1291 &TimeOut,
1292 newSID);
1293 if (ret != UPNP_E_SUCCESS) {
1294 msg << "Error Subscribing to EventURL";
1295 UPnP::ProcessErrorMessage(
1296 msg.str(), es_event->ErrCode, NULL, NULL);
1297 } else {
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 == '" <<
1307 newSID << "'.";
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
1311 // service, so...
1312 upnpCP->RefreshPortMappings();
1313 } else {
1314 msg << "Error: did not find service " <<
1315 newSID << " in the service map.";
1316 AddDebugLogLineC(logUPnP, msg);
1319 break;
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);
1331 } else {
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.
1340 break;
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);
1351 } else {
1352 #if 0
1353 // Warning: The use of UpnpGetServiceVarStatus and
1354 // UpnpGetServiceVarStatusAsync is deprecated by the
1355 // UPnP forum.
1356 TvCtrlPointHandleGetVar(
1357 sv_event->CtrlUrl,
1358 sv_event->StateVarName,
1359 sv_event->CurrentVal );
1360 #endif
1362 break;
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);
1379 break;
1380 default:
1381 // Humm, this is not good, we forgot to handle something...
1382 fprintf(stderr,
1383 "Callback: default... Unknown event:'%d', not good.\n",
1384 EventType);
1385 msg << "error(UPnP::Callback): Event not handled:'" <<
1386 EventType << "'.";
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);
1391 break;
1394 return 0;
1398 void CUPnPControlPoint::OnEventReceived(
1399 const std::string &Sid,
1400 int EventKey,
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);
1410 if (child) {
1411 while (child) {
1412 IXML_Element *child2 = IXML::Element::GetFirstChild(child);
1413 const DOMString childTag = IXML::Element::GetTag(child2);
1414 std::string childValue = IXML::Element::GetTextValue(child2);
1415 msg << "\n " <<
1416 childTag << "='" <<
1417 childValue << "'";
1418 child = IXML::Element::GetNextSibling(child);
1420 } else {
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() ?
1437 location :
1438 OriginalURLBase);
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();
1444 if (alreadyAdded) {
1445 // Just set the expires field
1446 it->second->SetExpires(expires);
1447 } else {
1448 // Add a new root device to the root device list
1449 CUPnPRootDevice *upnpRootDevice = new CUPnPRootDevice(
1450 *this, rootDevice,
1451 OriginalURLBase, FixedURLBase,
1452 location, expires);
1453 m_RootDeviceMap[upnpRootDevice->GetUDN()] = upnpRootDevice;
1458 void CUPnPControlPoint::RemoveRootDevice(const char *udn)
1460 // Lock the Root Device List
1461 CUPnPMutexLocker lock(m_RootDeviceListMutex);
1463 // Remove
1464 std::string UDN(udn);
1465 RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
1466 if (it != m_RootDeviceMap.end()) {
1467 delete it->second;
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);
1491 msg.str("");
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(),
1499 service.GetSID());
1500 if (errcode == UPNP_E_SUCCESS) {
1501 msg << "Successfully subscribed to service " <<
1502 service.GetServiceType() << ", absEventSubURL: " <<
1503 service.GetAbsEventSubURL() << ".";
1504 AddDebugLogLineC(logUPnP, msg);
1505 } else {
1506 msg << "Error subscribing to service " <<
1507 service.GetServiceType() << ", absEventSubURL: " <<
1508 service.GetAbsEventSubURL() << ", error: " <<
1509 UpnpGetErrorMessage(errcode) << ".";
1510 goto error;
1512 } else {
1513 msg << "Error getting SCPD Document from " <<
1514 service.GetAbsSCPDURL() << ".";
1515 AddDebugLogLineC(logUPnP, msg);
1518 return;
1520 // Error processing
1521 error:
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 */