Removed HAVE_CONFIG_H
[amule.git] / src / UPnPBase.cpp
blob6363fcb831a29632a7739477b3c343672659b214
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 #include "amule-config.h" // Needed for ENABLE_UPNP
28 #ifdef ENABLE_UPNP
30 // check for broken Debian-hacked libUPnP
31 #include <upnp.h>
32 #ifdef STRING_H // defined in UpnpString.h Yes, I would have liked UPNPSTRING_H much better.
33 #define BROKEN_DEBIAN_LIBUPNP
34 #endif
36 #include "UPnPBase.h"
38 #include <algorithm> // For transform()
40 #ifdef BROKEN_DEBIAN_LIBUPNP
41 #define GET_UPNP_STRING(a) UpnpString_get_String(a)
42 #else
43 #define GET_UPNP_STRING(a) (a)
44 #endif
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";
61 /**
62 * Case insensitive std::string comparison
64 static bool stdStringIsEqualCI(const std::string &s1, const std::string &s2)
66 std::string ns1(s1);
67 std::string ns2(s2);
68 std::transform(ns1.begin(), ns1.end(), ns1.begin(), tolower);
69 std::transform(ns2.begin(), ns2.end(), ns2.begin(), tolower);
70 return ns1 == ns2;
74 CUPnPPortMapping::CUPnPPortMapping(
75 int port,
76 const std::string &protocol,
77 bool enabled,
78 const std::string &description)
80 m_port(),
81 m_protocol(protocol),
82 m_enabled(enabled ? "1" : "0"),
83 m_description(description),
84 m_key()
86 std::ostringstream oss;
87 oss << port;
88 m_port = oss.str();
89 m_key = m_protocol + m_port;
92 namespace UPnP {
94 static const std::string ROOT_DEVICE("upnp:rootdevice");
96 namespace Device {
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");
103 namespace Service {
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,
112 int errorCode,
113 const DOMString errorString,
114 IXML_Document *doc)
116 std::ostringstream msg;
117 if (errorString == NULL || *errorString == 0) {
118 errorString = "Not available";
120 if (errorCode > 0) {
121 msg << "Error: " <<
122 messsage <<
123 ": Error code :'";
124 if (doc) {
125 CUPnPError e(doc);
126 msg << e.getErrorCode() <<
127 "', Error description :'" <<
128 e.getErrorDescription() <<
129 "'.";
130 } else {
131 msg << errorCode <<
132 "', Error description :'" <<
133 errorString <<
134 "'.";
136 AddDebugLogLineN(logUPnP, msg);
137 } else {
138 msg << "Error: " <<
139 messsage <<
140 ": UPnP SDK error: " <<
141 UpnpGetErrorMessage(errorCode) <<
142 " (" << errorCode << ").";
143 AddDebugLogLineN(logUPnP, msg);
146 return msg.str();
150 static void ProcessActionResponse(
151 IXML_Document *RespDoc,
152 const std::string &actionName)
154 std::ostringstream msg;
155 msg << "Response: ";
156 IXML_Element *root = IXML::Document::GetRootElement(RespDoc);
157 IXML_Element *child = IXML::Element::GetFirstChild(root);
158 if (child) {
159 while (child) {
160 const DOMString childTag = IXML::Element::GetTag(child);
161 std::string childValue = IXML::Element::GetTextValue(child);
162 msg << "\n " <<
163 childTag << "='" <<
164 childValue << "'";
165 child = IXML::Element::GetNextSibling(child);
167 } else {
168 msg << "\n Empty response for action '" <<
169 actionName << "'.";
171 AddDebugLogLineN(logUPnP, msg);
174 } /* namespace UPnP */
177 namespace IXML {
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);
198 namespace Element {
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)
233 if (!element) {
234 return stdEmptyString;
236 IXML_Node *text = ixmlNode_getFirstChild(&element->n);
237 const DOMString s = ixmlNode_getNodeValue(text);
238 std::string ret;
239 if (s) {
240 ret = s;
243 return ret;
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
258 * NULL if not found.
260 IXML_Element *GetFirstChildByTag(IXML_Element *element, const DOMString tag)
262 if (!element || !tag) {
263 return NULL;
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) {
284 return NULL;
287 IXML_Node *child = &element->n;
288 const DOMString childTag = NULL;
289 do {
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);
303 std::string ret;
304 if (s) {
305 ret = s;
307 ixmlNamedNodeMap_free(NamedNodeMap);
309 return ret;
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,
398 IXML_Element *scpd,
399 const std::string &SCPDURL)
401 m_ActionList(upnpControlPoint, scpd, SCPDURL),
402 m_ServiceStateTable(upnpControlPoint, scpd, SCPDURL),
403 m_SCPDURL(SCPDURL)
408 CUPnPArgumentValue::CUPnPArgumentValue()
410 m_argument(),
411 m_value()
416 CUPnPArgumentValue::CUPnPArgumentValue(
417 const std::string &argument, const std::string &value)
419 m_argument(argument),
420 m_value(value)
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")),
436 m_timeout(1801),
437 m_SCPD(nullptr)
439 std::ostringstream msg;
440 int errcode;
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(
447 URLBase.c_str(),
448 m_SCPDURL.c_str(),
449 scpdURL);
450 if( errcode != UPNP_E_SUCCESS ) {
451 msg << "Error generating scpdURL from " <<
452 "|" << URLBase << "|" <<
453 m_SCPDURL << "|.";
454 AddDebugLogLineN(logUPnP, msg);
455 } else {
456 m_absSCPDURL = scpdURL;
459 std::vector<char> vcontrolURL(
460 URLBase.length() + m_controlURL.length() + 1);
461 char *controlURL = &vcontrolURL[0];
462 errcode = UpnpResolveURL(
463 URLBase.c_str(),
464 m_controlURL.c_str(),
465 controlURL);
466 if( errcode != UPNP_E_SUCCESS ) {
467 msg << "Error generating controlURL from " <<
468 "|" << URLBase << "|" <<
469 m_controlURL << "|.";
470 AddDebugLogLineN(logUPnP, msg);
471 } else {
472 m_absControlURL = controlURL;
475 std::vector<char> veventURL(
476 URLBase.length() + m_eventSubURL.length() + 1);
477 char *eventURL = &veventURL[0];
478 errcode = UpnpResolveURL(
479 URLBase.c_str(),
480 m_eventSubURL.c_str(),
481 eventURL);
482 if( errcode != UPNP_E_SUCCESS ) {
483 msg << "Error generating eventURL from " <<
484 "|" << URLBase << "|" <<
485 m_eventSubURL << "|.";
486 AddDebugLogLineN(logUPnP, msg);
487 } else {
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) {
504 #if 0
505 m_serviceType == UPnP::Service::WAN_PPP_Connection ||
506 m_serviceType == UPnP::Service::WAN_Common_Interface_Config ||
507 m_serviceType == UPnP::Service::Layer3_Forwarding) {
508 #endif
509 #if 0
510 //#warning Delete this code on release.
511 if (!upnpControlPoint.WanServiceDetected()) {
512 // This condition can be used to suspend the parse
513 // of the XML tree.
514 #endif
515 //#warning Delete this code when m_WanService is no longer used.
516 const_cast<CUPnPControlPoint &>(upnpControlPoint).SetWanService(this);
517 // Log it
518 msg.str("");
519 msg << "WAN Service Detected: '" <<
520 m_serviceType << "'.";
521 AddDebugLogLineC(logUPnP, msg);
522 // Subscribe
523 const_cast<CUPnPControlPoint &>(upnpControlPoint).Subscribe(*this);
524 #if 0
525 //#warning Delete this code on release.
526 } else {
527 msg.str("");
528 msg << "WAN service detected again: '" <<
529 m_serviceType <<
530 "'. Will only use the first instance.";
531 AddDebugLogLineC(logUPnP, msg);
533 #endif
534 } else {
535 msg.str("");
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);
557 return false;
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);
567 return false;
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);
581 return false;
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);
591 return false;
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);
606 return false;
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);
618 return false;
621 if (firstTime) {
622 firstTime = false;
623 } else {
624 msgAction << ", ";
626 msgAction <<
627 ArgValue[i].GetArgument() <<
628 "='" <<
629 ArgValue[i].GetValue() <<
630 "'";
632 msgAction << ")";
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(
639 &ActionDoc,
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);
647 return false;
650 } else {
651 ActionDoc = UpnpMakeAction(
652 action.GetName().c_str(),
653 GetServiceType().c_str(),
654 0, NULL);
655 if (!ActionDoc) {
656 msg << "Error: UpnpMakeAction returned NULL.";
657 AddDebugLogLineN(logUPnP, msg);
658 return false;
661 #if 0
662 // Send the action asynchronously
663 UpnpSendActionAsync(
664 m_UPnPControlPoint.GetUPnPClientHandle(),
665 GetAbsControlURL().c_str(),
666 GetServiceType().c_str(),
667 NULL, ActionDoc,
668 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
669 NULL);
670 return true;
671 #endif
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);
685 return false;
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);
695 return true;
699 const std::string CUPnPService::GetStateVariable(
700 const std::string &stateVariableName) const
702 std::ostringstream msg;
703 DOMString StVarVal;
704 int ret = UpnpGetServiceVarStatus(
705 m_UPnPControlPoint.GetUPnPClientHandle(),
706 GetAbsControlURL().c_str(),
707 stateVariableName.c_str(),
708 &StVarVal);
709 if (ret != UPNP_E_SUCCESS) {
710 msg << "GetStateVariable(\"" <<
711 stateVariableName <<
712 "\"): in a call to UpnpGetServiceVarStatus";
713 UPnP::ProcessErrorMessage(
714 msg.str(), ret, StVarVal, NULL);
715 return stdEmptyString;
717 msg << "GetStateVariable: " <<
718 stateVariableName <<
719 "='" <<
720 StVarVal <<
721 "'.";
722 AddDebugLogLineN(logUPnP, msg);
723 return StVarVal;
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(
753 URLBase.c_str(),
754 m_presentationURL.c_str(),
755 presURL);
756 if (errcode != UPNP_E_SUCCESS) {
757 msg << "Error generating presentationURL from " <<
758 "|" << URLBase << "|" <<
759 m_presentationURL << "|.";
760 AddDebugLogLineN(logUPnP, msg);
761 } else {
762 m_presentationURL = presURL;
765 msg.str("");
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,
789 int expires)
791 CUPnPDevice(upnpControlPoint, rootDevice, FixedURLBase),
792 m_URLBase(OriginalURLBase),
793 m_location(location),
794 m_expires(expires)
796 std::ostringstream msg;
797 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(),
813 m_RootDeviceMap(),
814 m_ServiceMap(),
815 m_ActivePortMappingsMap(),
816 m_RootDeviceListMutex(),
817 m_IGWDeviceDetected(false),
818 m_WanService(NULL)
820 // Pointer to self
821 s_CtrlPoint = this;
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’"
828 unsigned short port;
829 char *ipAddress;
831 // Start UPnP
832 int ret;
833 ret = UpnpInit2(0, udpPort);
834 if (ret != UPNP_E_SUCCESS) {
835 msg << "error(UpnpInit2): Error code ";
836 goto error;
838 port = UpnpGetServerPort();
839 ipAddress = UpnpGetServerIpAddress();
840 msg << "bound to " << ipAddress << ":" <<
841 port << ".";
842 AddDebugLogLineN(logUPnP, msg);
843 msg.str("");
844 ret = UpnpRegisterClient(
845 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
846 &m_UPnPClientHandle,
847 &m_UPnPClientHandle);
848 if (ret != UPNP_E_SUCCESS) {
849 msg << "error(UpnpRegisterClient): Error registering callback: ";
850 goto error;
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
861 // on the mutex.
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: ";
868 goto error;
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
878 // callback.
879 CUPnPMutexLocker lock(m_WaitForSearchTimeoutMutex);
881 return;
883 // Error processing
884 error:
885 UpnpFinish();
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();
895 ++it) {
896 delete it->second;
898 // Remove all first
899 // RemoveAll();
900 UpnpUnRegisterClient(m_UPnPClientHandle);
901 UpnpFinish();
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);
914 return false;
917 int n = upnpPortMapping.size();
918 bool ok = false;
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()] =
933 upnpPortMapping[i];
935 // Add the port mapping
936 PrivateAddPortMapping(upnpPortMapping[i]);
940 // Test some variables, this is deprecated, might not work
941 // with some routers
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");
953 // Just for testing
954 std::vector<CUPnPArgumentValue> argval;
955 argval.resize(0);
956 m_WanService->Execute("GetStatusInfo", argval);
958 #if 0
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");
968 #endif
970 // Debug only
971 msg.str("");
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;
985 return ok;
989 void CUPnPControlPoint::RefreshPortMappings()
991 for ( PortMappingMap::iterator it = m_ActivePortMappingsMap.begin();
992 it != m_ActivePortMappingsMap.end();
993 ++it) {
994 PrivateAddPortMapping(it->second);
997 // For testing
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");
1030 // Execute
1031 bool ret = true;
1032 for (ServiceMap::iterator it = m_ServiceMap.begin();
1033 it != m_ServiceMap.end(); ++it) {
1034 ret &= it->second->Execute(actionName, argval);
1037 return ret;
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);
1050 return false;
1053 int n = upnpPortMapping.size();
1054 bool ok = false;
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);
1073 } else {
1074 msg << "UPnP Error: "
1075 "CUPnPControlPoint::DeletePortMapping: "
1076 "Mapping was not found in the active "
1077 "mapping map.";
1078 AddDebugLogLineC(logUPnP, msg);
1081 // Delete the port mapping
1082 PrivateDeletePortMapping(upnpPortMapping[i]);
1086 // Debug only
1087 msg.str("");
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;
1101 return ok;
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());
1120 // Execute
1121 bool ret = true;
1122 for (ServiceMap::iterator it = m_ServiceMap.begin();
1123 it != m_ServiceMap.end(); ++it) {
1124 ret &= it->second->Execute(actionName, argval);
1127 return ret;
1131 // This function is static
1132 #if UPNP_VERSION >= 10800
1133 int CUPnPControlPoint::Callback(Upnp_EventType_e EventType, const void *Event, void * /*Cookie*/)
1134 #else
1135 int CUPnPControlPoint::Callback(Upnp_EventType EventType, void *Event, void * /*Cookie*/)
1136 #endif
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: ";
1151 goto upnpDiscovery;
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: ";
1156 // UPnP Discovery
1157 upnpDiscovery:
1158 #if UPNP_VERSION >= 10800
1159 UpnpDiscovery *d_event = (UpnpDiscovery *)Event;
1160 #else
1161 struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
1162 #endif
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) << ".";
1168 #else
1169 int ret;
1170 if (d_event->ErrCode != UPNP_E_SUCCESS) {
1171 msg << UpnpGetErrorMessage(d_event->ErrCode) << ".";
1172 #endif
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);
1179 #else
1180 ret = UpnpDownloadXmlDoc(d_event->Location, &doc);
1181 #endif
1182 if (ret != UPNP_E_SUCCESS) {
1183 msg << "Error retrieving device description from " <<
1184 #if UPNP_VERSION >= 10800
1185 location << ": " <<
1186 #else
1187 d_event->Location << ": " <<
1188 #endif
1189 UpnpGetErrorMessage(ret) <<
1190 "(" << ret << ").";
1191 AddDebugLogLineC(logUPnP, msg);
1192 } else {
1193 msg2 << "Retrieving device description from " <<
1194 #if UPNP_VERSION >= 10800
1195 location << ".";
1196 #else
1197 d_event->Location << ".";
1198 #endif
1199 AddDebugLogLineN(logUPnP, msg2);
1201 if (doc) {
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,
1229 location, expires);
1230 #else
1231 upnpCP->AddRootDevice(rootDevice, urlBase,
1232 d_event->Location, d_event->Expires);
1233 #endif
1235 // Free the XML doc tree
1236 IXML::Document::Free(doc);
1238 break;
1240 case UPNP_DISCOVERY_SEARCH_TIMEOUT: {
1241 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1242 // Search timeout
1243 msg << "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1244 AddDebugLogLineN(logUPnP, msg);
1246 // Unlock the search timeout mutex
1247 upnpCP->m_WaitForSearchTimeoutMutex.Unlock();
1249 break;
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) {
1258 #else
1259 struct Upnp_Discovery *dab_event = (struct Upnp_Discovery *)Event;
1260 if (dab_event->ErrCode != UPNP_E_SUCCESS) {
1261 #endif
1262 msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
1263 #if UPNP_VERSION >= 10800
1264 UpnpGetErrorMessage(errCode) <<
1265 #else
1266 UpnpGetErrorMessage(dab_event->ErrCode) <<
1267 #endif
1268 ".";
1269 AddDebugLogLineC(logUPnP, msg);
1271 #if UPNP_VERSION >= 10800
1272 std::string devType = UpnpDiscovery_get_DeviceType_cstr(dab_event);
1273 #else
1274 std::string devType = dab_event->DeviceType;
1275 #endif
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);
1285 #else
1286 upnpCP->RemoveRootDevice(dab_event->DeviceId);
1287 #endif
1289 break;
1291 case UPNP_EVENT_RECEIVED: {
1292 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1293 // Event reveived
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);
1300 #else
1301 struct Upnp_Event *e_event = (struct Upnp_Event *)Event;
1302 const std::string Sid = e_event->Sid;
1303 #endif
1304 // Parses the event
1305 #if UPNP_VERSION >= 10800
1306 upnpCP->OnEventReceived(sid, eventKey, changedVariables);
1307 #else
1308 upnpCP->OnEventReceived(Sid, e_event->EventKey, e_event->ChangedVariables);
1309 #endif
1310 break;
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) {
1328 #else
1329 struct Upnp_Event_Subscribe *es_event =
1330 (struct Upnp_Event_Subscribe *)Event;
1331 if (es_event->ErrCode != UPNP_E_SUCCESS) {
1332 #endif
1333 msg << "Error in Event Subscribe Callback";
1334 #if UPNP_VERSION >= 10800
1335 UPnP::ProcessErrorMessage(msg.str(), errCode, NULL, NULL);
1336 #else
1337 UPnP::ProcessErrorMessage(
1338 msg.str(), es_event->ErrCode, NULL, NULL);
1339 #endif
1340 } else {
1341 #if 0
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);
1350 #else
1351 TvCtrlPointHandleSubscribeUpdate(
1352 GET_UPNP_STRING(es_event->PublisherUrl),
1353 es_event->Sid,
1354 es_event->TimeOut );
1355 #endif
1356 #endif
1358 break;
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;
1372 #else
1373 struct Upnp_Event_Subscribe *es_event =
1374 (struct Upnp_Event_Subscribe *)Event;
1375 #endif
1376 Upnp_SID newSID;
1377 memset(newSID, 0, sizeof(Upnp_SID));
1378 int TimeOut = 1801;
1379 #if UPNP_VERSION >= 10800
1380 const char *publisherUrl =
1381 UpnpEventSubscribe_get_PublisherUrl_cstr(es_event);
1382 #endif
1383 int ret = UpnpSubscribe(
1384 upnpCP->m_UPnPClientHandle,
1385 #if UPNP_VERSION >= 10800
1386 publisherUrl,
1387 #else
1388 GET_UPNP_STRING(es_event->PublisherUrl),
1389 #endif
1390 &TimeOut,
1391 newSID);
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);
1396 #endif
1397 UPnP::ProcessErrorMessage(
1398 #if UPNP_VERSION >= 10800
1399 msg.str(), errCode, NULL, NULL);
1400 #else
1401 msg.str(), es_event->ErrCode, NULL, NULL);
1402 #endif
1403 } else {
1404 ServiceMap::iterator it =
1405 #if UPNP_VERSION >= 10800
1406 upnpCP->m_ServiceMap.find(publisherUrl);
1407 #else
1408 upnpCP->m_ServiceMap.find(GET_UPNP_STRING(es_event->PublisherUrl));
1409 #endif
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
1416 publisherUrl <<
1417 #else
1418 GET_UPNP_STRING(es_event->PublisherUrl) <<
1419 #endif
1420 "' with SID == '" <<
1421 newSID << "'.";
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
1425 // service, so...
1426 upnpCP->RefreshPortMappings();
1427 } else {
1428 msg << "Error: did not find service " <<
1429 newSID << " in the service map.";
1430 AddDebugLogLineC(logUPnP, msg);
1433 break;
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) {
1444 #else
1445 struct Upnp_Action_Complete *a_event =
1446 (struct Upnp_Action_Complete *)Event;
1447 if (a_event->ErrCode != UPNP_E_SUCCESS) {
1448 #endif
1449 UPnP::ProcessErrorMessage(
1450 "UpnpSendActionAsync",
1451 #if UPNP_VERSION >= 10800
1452 errCode, NULL,
1453 actionResult);
1454 #else
1455 a_event->ErrCode, NULL,
1456 a_event->ActionResult);
1457 #endif
1458 } else {
1459 // Check the response document
1460 UPnP::ProcessActionResponse(
1461 #if UPNP_VERSION >= 10800
1462 actionResult,
1463 #else
1464 a_event->ActionResult,
1465 #endif
1466 "<UpnpSendActionAsync>");
1468 /* No need for any processing here, just print out results.
1469 * Service state table updates are handled by events.
1471 break;
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) {
1480 #else
1481 struct Upnp_State_Var_Complete *sv_event =
1482 (struct Upnp_State_Var_Complete *)Event;
1483 if (sv_event->ErrCode != UPNP_E_SUCCESS) {
1484 #endif
1485 msg << "m_UpnpGetServiceVarStatusAsync";
1486 UPnP::ProcessErrorMessage(
1487 #if UPNP_VERSION >= 10800
1488 msg.str(), errCode, NULL, NULL);
1489 #else
1490 msg.str(), sv_event->ErrCode, NULL, NULL);
1491 #endif
1492 } else {
1493 #if 0
1494 // Warning: The use of UpnpGetServiceVarStatus and
1495 // UpnpGetServiceVarStatusAsync is deprecated by the
1496 // UPnP forum.
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);
1506 #else
1507 TvCtrlPointHandleGetVar(
1508 sv_event->CtrlUrl,
1509 sv_event->StateVarName,
1510 sv_event->CurrentVal );
1511 #endif
1512 #endif
1514 break;
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);
1531 break;
1532 default:
1533 // Humm, this is not good, we forgot to handle something...
1534 fprintf(stderr,
1535 "Callback: default... Unknown event:'%d', not good.\n",
1536 EventType);
1537 msg << "error(UPnP::Callback): Event not handled:'" <<
1538 EventType << "'.";
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);
1543 break;
1546 return 0;
1550 void CUPnPControlPoint::OnEventReceived(
1551 const std::string &Sid,
1552 int EventKey,
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);
1562 if (child) {
1563 while (child) {
1564 IXML_Element *child2 = IXML::Element::GetFirstChild(child);
1565 const DOMString childTag = IXML::Element::GetTag(child2);
1566 std::string childValue = IXML::Element::GetTextValue(child2);
1567 msg << "\n " <<
1568 childTag << "='" <<
1569 childValue << "'";
1570 child = IXML::Element::GetNextSibling(child);
1572 } else {
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() ?
1589 location :
1590 OriginalURLBase);
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();
1596 if (alreadyAdded) {
1597 // Just set the expires field
1598 it->second->SetExpires(expires);
1599 } else {
1600 // Add a new root device to the root device list
1601 CUPnPRootDevice *upnpRootDevice = new CUPnPRootDevice(
1602 *this, rootDevice,
1603 OriginalURLBase, FixedURLBase,
1604 location, expires);
1605 m_RootDeviceMap[upnpRootDevice->GetUDN()] = upnpRootDevice;
1610 void CUPnPControlPoint::RemoveRootDevice(const char *udn)
1612 // Lock the Root Device List
1613 CUPnPMutexLocker lock(m_RootDeviceListMutex);
1615 // Remove
1616 std::string UDN(udn);
1617 RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
1618 if (it != m_RootDeviceMap.end()) {
1619 delete it->second;
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);
1643 msg.str("");
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(),
1651 service.GetSID());
1652 if (errcode == UPNP_E_SUCCESS) {
1653 msg << "Successfully subscribed to service " <<
1654 service.GetServiceType() << ", absEventSubURL: " <<
1655 service.GetAbsEventSubURL() << ".";
1656 AddDebugLogLineC(logUPnP, msg);
1657 } else {
1658 msg << "Error subscribing to service " <<
1659 service.GetServiceType() << ", absEventSubURL: " <<
1660 service.GetAbsEventSubURL() << ", error: " <<
1661 UpnpGetErrorMessage(errcode) << ".";
1662 goto error;
1664 } else {
1665 msg << "Error getting SCPD Document from " <<
1666 service.GetAbsSCPDURL() << ".";
1667 AddDebugLogLineC(logUPnP, msg);
1670 return;
1672 // Error processing
1673 error:
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 */