Upstream tarball 9574
[amule.git] / src / UPnPBase.cpp
blob4b042bc7b027fad607e5de01a6e9d32290c95296
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2004-2008 Marcelo Roberto Jimenez ( phoenix@amule.org )
5 // Copyright (c) 2006-2008 aMule Team ( admin@amule.org / http://www.amule.org )
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.
20 //
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 #define UPNP_C
28 #include "UPnPBase.h"
31 #include <dlfcn.h> // For dlopen(), dlsym(), dlclose()
32 #include <algorithm> // For transform()
35 #ifdef __GNUC__
36 #if __GNUC__ >= 4
37 #define REINTERPRET_CAST(x) reinterpret_cast<x>
38 #endif
39 #endif
40 #ifndef REINTERPRET_CAST
41 // Let's hope that function pointers are equal in size to data pointers
42 #define REINTERPRET_CAST(x) (x)
43 #endif
46 /**
47 * Case insensitive std::string comparison
49 bool stdStringIsEqualCI(const std::string &s1, const std::string &s2)
51 std::string ns1(s1);
52 std::string ns2(s2);
53 std::transform(ns1.begin(), ns1.end(), ns1.begin(), tolower);
54 std::transform(ns2.begin(), ns2.end(), ns2.begin(), tolower);
55 return ns1 == ns2;
59 CUPnPPortMapping::CUPnPPortMapping(
60 int port,
61 const std::string &protocol,
62 bool enabled,
63 const std::string &description)
65 m_port(),
66 m_protocol(protocol),
67 m_enabled(enabled ? "1" : "0"),
68 m_description(description),
69 m_key()
71 std::ostringstream oss;
72 oss << port;
73 m_port = oss.str();
74 m_key = m_protocol + m_port;
78 const std::string &CUPnPLib::UPNP_ROOT_DEVICE =
79 "upnp:rootdevice";
81 const std::string &CUPnPLib::UPNP_DEVICE_IGW =
82 "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
83 const std::string &CUPnPLib::UPNP_DEVICE_WAN =
84 "urn:schemas-upnp-org:device:WANDevice:1";
85 const std::string &CUPnPLib::UPNP_DEVICE_WAN_CONNECTION =
86 "urn:schemas-upnp-org:device:WANConnectionDevice:1";
87 const std::string &CUPnPLib::UPNP_DEVICE_LAN =
88 "urn:schemas-upnp-org:device:LANDevice:1";
90 const std::string &CUPnPLib::UPNP_SERVICE_LAYER3_FORWARDING =
91 "urn:schemas-upnp-org:service:Layer3Forwarding:1";
92 const std::string &CUPnPLib::UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG =
93 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1";
94 const std::string &CUPnPLib::UPNP_SERVICE_WAN_IP_CONNECTION =
95 "urn:schemas-upnp-org:service:WANIPConnection:1";
96 const std::string &CUPnPLib::UPNP_SERVICE_WAN_PPP_CONNECTION =
97 "urn:schemas-upnp-org:service:WANPPPConnection:1";
100 CUPnPLib::CUPnPLib(CUPnPControlPoint &ctrlPoint)
102 m_ctrlPoint(ctrlPoint)
107 std::string CUPnPLib::GetUPnPErrorMessage(int code) const
109 return UpnpGetErrorMessage(code);
113 std::string CUPnPLib::processUPnPErrorMessage(
114 const std::string &messsage,
115 int errorCode,
116 const DOMString errorString,
117 IXML_Document *doc) const
119 std::ostringstream msg;
120 if (errorString == NULL || *errorString == 0) {
121 errorString = "Not available";
123 if (errorCode > 0) {
124 msg << "Error: " <<
125 messsage <<
126 ": Error code :'";
127 if (doc) {
128 CUPnPError e(*this, doc);
129 msg << e.getErrorCode() <<
130 "', Error description :'" <<
131 e.getErrorDescription() <<
132 "'.";
133 } else {
134 msg << errorCode <<
135 "', Error description :'" <<
136 errorString <<
137 "'.";
139 AddLogLineU(false, logUPnP, msg);
140 } else {
141 msg << "Error: " <<
142 messsage <<
143 ": UPnP SDK error: " <<
144 GetUPnPErrorMessage(errorCode) <<
145 " (" << errorCode << ").";
146 AddLogLineU(false, logUPnP, msg);
149 return msg.str();
153 void CUPnPLib::ProcessActionResponse(
154 IXML_Document *RespDoc,
155 const std::string &actionName) const
157 std::ostringstream msg;
158 msg << "Response: ";
159 IXML_Element *root = Element_GetRootElement(RespDoc);
160 IXML_Element *child = Element_GetFirstChild(root);
161 if (child) {
162 while (child) {
163 const DOMString childTag = Element_GetTag(child);
164 std::string childValue = Element_GetTextValue(child);
165 msg << "\n " <<
166 childTag << "='" <<
167 childValue << "'";
168 child = Element_GetNextSibling(child);
170 } else {
171 msg << "\n Empty response for action '" <<
172 actionName << "'.";
174 AddDebugLogLineM(false, logUPnP, msg);
179 * \brief Returns the root node of a given document.
181 IXML_Element *CUPnPLib::Element_GetRootElement(
182 IXML_Document *doc) const
184 IXML_Element *root = REINTERPRET_CAST(IXML_Element *)(
185 ixmlNode_getFirstChild(
186 REINTERPRET_CAST(IXML_Node *)(doc)));
188 return root;
193 * \brief Returns the first child of a given element.
195 IXML_Element *CUPnPLib::Element_GetFirstChild(
196 IXML_Element *parent) const
198 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(parent);
199 IXML_Node *child = ixmlNode_getFirstChild(node);
201 return REINTERPRET_CAST(IXML_Element *)(child);
206 * \brief Returns the next sibling of a given child.
208 IXML_Element *CUPnPLib::Element_GetNextSibling(
209 IXML_Element *child) const
211 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(child);
212 IXML_Node *sibling = ixmlNode_getNextSibling(node);
214 return REINTERPRET_CAST(IXML_Element *)(sibling);
219 * \brief Returns the element tag (name)
221 const DOMString CUPnPLib::Element_GetTag(
222 IXML_Element *element) const
224 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element);
225 const DOMString tag = ixmlNode_getNodeName(node);
227 return tag;
232 * \brief Returns the TEXT node value of the current node.
234 const std::string CUPnPLib::Element_GetTextValue(
235 IXML_Element *element) const
237 if (!element) {
238 return stdEmptyString;
240 IXML_Node *text = ixmlNode_getFirstChild(
241 REINTERPRET_CAST(IXML_Node *)(element));
242 const DOMString s = ixmlNode_getNodeValue(text);
243 std::string ret;
244 if (s) {
245 ret = s;
248 return ret;
253 * \brief Returns the TEXT node value of the first child matching tag.
255 const std::string CUPnPLib::Element_GetChildValueByTag(
256 IXML_Element *element,
257 const DOMString tag) const
259 IXML_Element *child =
260 Element_GetFirstChildByTag(element, tag);
262 return Element_GetTextValue(child);
267 * \brief Returns the first child element that matches the requested tag or
268 * NULL if not found.
270 IXML_Element *CUPnPLib::Element_GetFirstChildByTag(
271 IXML_Element *element,
272 const DOMString tag) const
274 if (!element || !tag) {
275 return NULL;
278 IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element);
279 IXML_Node *child = ixmlNode_getFirstChild(node);
280 const DOMString childTag = ixmlNode_getNodeName(child);
281 while(child && childTag && strcmp(tag, childTag)) {
282 child = ixmlNode_getNextSibling(child);
283 childTag = ixmlNode_getNodeName(child);
286 return REINTERPRET_CAST(IXML_Element *)(child);
291 * \brief Returns the next sibling element that matches the requested tag. Should be
292 * used with the return value of Element_GetFirstChildByTag().
294 IXML_Element *CUPnPLib::Element_GetNextSiblingByTag(
295 IXML_Element *element, const DOMString tag) const
297 if (!element || !tag) {
298 return NULL;
301 IXML_Node *child = REINTERPRET_CAST(IXML_Node *)(element);
302 const DOMString childTag = NULL;
303 do {
304 child = ixmlNode_getNextSibling(child);
305 childTag = ixmlNode_getNodeName(child);
306 } while(child && childTag && strcmp(tag, childTag));
308 return REINTERPRET_CAST(IXML_Element *)(child);
312 const std::string CUPnPLib::Element_GetAttributeByTag(
313 IXML_Element *element, const DOMString tag) const
315 IXML_NamedNodeMap *NamedNodeMap = ixmlNode_getAttributes(
316 REINTERPRET_CAST(IXML_Node *)(element));
317 IXML_Node *attribute = ixmlNamedNodeMap_getNamedItem(NamedNodeMap, tag);
318 const DOMString s = ixmlNode_getNodeValue(attribute);
319 std::string ret;
320 if (s) {
321 ret = s;
323 ixmlNamedNodeMap_free(NamedNodeMap);
325 return ret;
329 CUPnPError::CUPnPError(
330 const CUPnPLib &upnpLib,
331 IXML_Document *errorDoc)
333 m_root (upnpLib.Element_GetRootElement(errorDoc)),
334 m_ErrorCode (upnpLib.Element_GetChildValueByTag(m_root, "errorCode")),
335 m_ErrorDescription(upnpLib.Element_GetChildValueByTag(m_root, "errorDescription"))
340 CUPnPArgument::CUPnPArgument(
341 const CUPnPControlPoint &upnpControlPoint,
342 CUPnPLib &upnpLib,
343 IXML_Element *argument,
344 const std::string &WXUNUSED(SCPDURL))
346 m_UPnPControlPoint(upnpControlPoint),
347 m_name (upnpLib.Element_GetChildValueByTag(argument, "name")),
348 m_direction (upnpLib.Element_GetChildValueByTag(argument, "direction")),
349 m_retval (upnpLib.Element_GetFirstChildByTag(argument, "retval")),
350 m_relatedStateVariable(upnpLib.Element_GetChildValueByTag(argument, "relatedStateVariable"))
352 std::ostringstream msg;
353 msg << "\n Argument:" <<
354 "\n name: " << m_name <<
355 "\n direction: " << m_direction <<
356 "\n retval: " << m_retval <<
357 "\n relatedStateVariable: " << m_relatedStateVariable;
358 AddDebugLogLineM(false, logUPnP, msg);
362 CUPnPAction::CUPnPAction(
363 const CUPnPControlPoint &upnpControlPoint,
364 CUPnPLib &upnpLib,
365 IXML_Element *action,
366 const std::string &SCPDURL)
368 m_UPnPControlPoint(upnpControlPoint),
369 m_ArgumentList(upnpControlPoint, upnpLib, action, SCPDURL),
370 m_name(upnpLib.Element_GetChildValueByTag(action, "name"))
372 std::ostringstream msg;
373 msg << "\n Action:" <<
374 "\n name: " << m_name;
375 AddDebugLogLineM(false, logUPnP, msg);
379 CUPnPAllowedValue::CUPnPAllowedValue(
380 const CUPnPControlPoint &upnpControlPoint,
381 CUPnPLib &upnpLib,
382 IXML_Element *allowedValue,
383 const std::string &WXUNUSED(SCPDURL))
385 m_UPnPControlPoint(upnpControlPoint),
386 m_allowedValue(upnpLib.Element_GetTextValue(allowedValue))
388 std::ostringstream msg;
389 msg << "\n AllowedValue:" <<
390 "\n allowedValue: " << m_allowedValue;
391 AddDebugLogLineM(false, logUPnP, msg);
395 CUPnPStateVariable::CUPnPStateVariable(
396 const CUPnPControlPoint &upnpControlPoint,
397 CUPnPLib &upnpLib,
398 IXML_Element *stateVariable,
399 const std::string &SCPDURL)
401 m_UPnPControlPoint(upnpControlPoint),
402 m_AllowedValueList(upnpControlPoint, upnpLib, stateVariable, SCPDURL),
403 m_name (upnpLib.Element_GetChildValueByTag(stateVariable, "name")),
404 m_dataType (upnpLib.Element_GetChildValueByTag(stateVariable, "dataType")),
405 m_defaultValue(upnpLib.Element_GetChildValueByTag(stateVariable, "defaultValue")),
406 m_sendEvents (upnpLib.Element_GetAttributeByTag (stateVariable, "sendEvents"))
408 std::ostringstream msg;
409 msg << "\n StateVariable:" <<
410 "\n name: " << m_name <<
411 "\n dataType: " << m_dataType <<
412 "\n defaultValue: " << m_defaultValue <<
413 "\n sendEvents: " << m_sendEvents;
414 AddDebugLogLineM(false, logUPnP, msg);
418 CUPnPSCPD::CUPnPSCPD(
419 const CUPnPControlPoint &upnpControlPoint,
420 CUPnPLib &upnpLib,
421 IXML_Element *scpd,
422 const std::string &SCPDURL)
424 m_UPnPControlPoint(upnpControlPoint),
425 m_ActionList(upnpControlPoint, upnpLib, scpd, SCPDURL),
426 m_ServiceStateTable(upnpControlPoint, upnpLib, scpd, SCPDURL),
427 m_SCPDURL(SCPDURL)
432 CUPnPArgumentValue::CUPnPArgumentValue()
434 m_argument(),
435 m_value()
440 CUPnPArgumentValue::CUPnPArgumentValue(
441 const std::string &argument, const std::string &value)
443 m_argument(argument),
444 m_value(value)
449 CUPnPService::CUPnPService(
450 const CUPnPControlPoint &upnpControlPoint,
451 CUPnPLib &upnpLib,
452 IXML_Element *service,
453 const std::string &URLBase)
455 m_UPnPControlPoint(upnpControlPoint),
456 m_upnpLib(upnpLib),
457 m_serviceType(upnpLib.Element_GetChildValueByTag(service, "serviceType")),
458 m_serviceId (upnpLib.Element_GetChildValueByTag(service, "serviceId")),
459 m_SCPDURL (upnpLib.Element_GetChildValueByTag(service, "SCPDURL")),
460 m_controlURL (upnpLib.Element_GetChildValueByTag(service, "controlURL")),
461 m_eventSubURL(upnpLib.Element_GetChildValueByTag(service, "eventSubURL")),
462 m_timeout(1801),
463 m_SCPD(NULL)
465 std::ostringstream msg;
466 int errcode;
468 std::vector<char> vscpdURL(URLBase.length() + m_SCPDURL.length() + 1);
469 char *scpdURL = &vscpdURL[0];
470 errcode = UpnpResolveURL(
471 URLBase.c_str(),
472 m_SCPDURL.c_str(),
473 scpdURL);
474 if( errcode != UPNP_E_SUCCESS ) {
475 msg << "Error generating scpdURL from " <<
476 "|" << URLBase << "|" <<
477 m_SCPDURL << "|.";
478 AddDebugLogLineM(false, logUPnP, msg);
479 } else {
480 m_absSCPDURL = scpdURL;
483 std::vector<char> vcontrolURL(
484 URLBase.length() + m_controlURL.length() + 1);
485 char *controlURL = &vcontrolURL[0];
486 errcode = UpnpResolveURL(
487 URLBase.c_str(),
488 m_controlURL.c_str(),
489 controlURL);
490 if( errcode != UPNP_E_SUCCESS ) {
491 msg << "Error generating controlURL from " <<
492 "|" << URLBase << "|" <<
493 m_controlURL << "|.";
494 AddDebugLogLineM(false, logUPnP, msg);
495 } else {
496 m_absControlURL = controlURL;
499 std::vector<char> veventURL(
500 URLBase.length() + m_eventSubURL.length() + 1);
501 char *eventURL = &veventURL[0];
502 errcode = UpnpResolveURL(
503 URLBase.c_str(),
504 m_eventSubURL.c_str(),
505 eventURL);
506 if( errcode != UPNP_E_SUCCESS ) {
507 msg << "Error generating eventURL from " <<
508 "|" << URLBase << "|" <<
509 m_eventSubURL << "|.";
510 AddDebugLogLineM(false, logUPnP, msg);
511 } else {
512 m_absEventSubURL = eventURL;
515 msg << "\n Service:" <<
516 "\n serviceType: " << m_serviceType <<
517 "\n serviceId: " << m_serviceId <<
518 "\n SCPDURL: " << m_SCPDURL <<
519 "\n absSCPDURL: " << m_absSCPDURL <<
520 "\n controlURL: " << m_controlURL <<
521 "\n absControlURL: " << m_absControlURL <<
522 "\n eventSubURL: " << m_eventSubURL <<
523 "\n absEventSubURL: " << m_absEventSubURL;
524 AddDebugLogLineM(false, logUPnP, msg);
526 if (m_serviceType == upnpLib.UPNP_SERVICE_WAN_IP_CONNECTION ||
527 m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION) {
528 #if 0
529 m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION ||
530 m_serviceType == upnpLib.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG ||
531 m_serviceType == upnpLib.UPNP_SERVICE_LAYER3_FORWARDING) {
532 #endif
533 #if 0
534 //#warning Delete this code on release.
535 if (!upnpLib.m_ctrlPoint.WanServiceDetected()) {
536 // This condition can be used to suspend the parse
537 // of the XML tree.
538 #endif
539 //#warning Delete this code when m_WanService is no longer used.
540 upnpLib.m_ctrlPoint.SetWanService(this);
541 // Log it
542 msg.str("");
543 msg << "WAN Service Detected: '" <<
544 m_serviceType << "'.";
545 AddDebugLogLineM(true, logUPnP, msg);
546 // Subscribe
547 upnpLib.m_ctrlPoint.Subscribe(*this);
548 #if 0
549 //#warning Delete this code on release.
550 } else {
551 msg.str("");
552 msg << "WAN service detected again: '" <<
553 m_serviceType <<
554 "'. Will only use the first instance.";
555 AddDebugLogLineM(true, logUPnP, msg);
557 #endif
558 } else {
559 msg.str("");
560 msg << "Uninteresting service detected: '" <<
561 m_serviceType << "'. Ignoring.";
562 AddDebugLogLineM(true, logUPnP, msg);
567 CUPnPService::~CUPnPService()
572 bool CUPnPService::Execute(
573 const std::string &ActionName,
574 const std::vector<CUPnPArgumentValue> &ArgValue) const
576 std::ostringstream msg;
577 if (m_SCPD.get() == NULL) {
578 msg << "Service without SCPD Document, cannot execute action '" << ActionName <<
579 "' for service '" << GetServiceType() << "'.";
580 AddDebugLogLineM(false, logUPnP, msg);
581 return false;
583 std::ostringstream msgAction("Sending action ");
584 // Check for correct action name
585 ActionList::const_iterator itAction =
586 m_SCPD->GetActionList().find(ActionName);
587 if (itAction == m_SCPD->GetActionList().end()) {
588 msg << "Invalid action name '" << ActionName <<
589 "' for service '" << GetServiceType() << "'.";
590 AddDebugLogLineM(false, logUPnP, msg);
591 return false;
593 msgAction << ActionName << "(";
594 bool firstTime = true;
595 // Check for correct Argument/Value pairs
596 const CUPnPAction &action = *(itAction->second);
597 for (unsigned int i = 0; i < ArgValue.size(); ++i) {
598 ArgumentList::const_iterator itArg =
599 action.GetArgumentList().find(ArgValue[i].GetArgument());
600 if (itArg == action.GetArgumentList().end()) {
601 msg << "Invalid argument name '" << ArgValue[i].GetArgument() <<
602 "' for action '" << action.GetName() <<
603 "' for service '" << GetServiceType() << "'.";
604 AddDebugLogLineM(false, logUPnP, msg);
605 return false;
607 const CUPnPArgument &argument = *(itArg->second);
608 if (tolower(argument.GetDirection()[0]) != 'i' ||
609 tolower(argument.GetDirection()[1]) != 'n') {
610 msg << "Invalid direction for argument '" <<
611 ArgValue[i].GetArgument() <<
612 "' for action '" << action.GetName() <<
613 "' for service '" << GetServiceType() << "'.";
614 AddDebugLogLineM(false, logUPnP, msg);
615 return false;
617 const std::string relatedStateVariableName =
618 argument.GetRelatedStateVariable();
619 if (!relatedStateVariableName.empty()) {
620 ServiceStateTable::const_iterator itSVT =
621 m_SCPD->GetServiceStateTable().
622 find(relatedStateVariableName);
623 if (itSVT == m_SCPD->GetServiceStateTable().end()) {
624 msg << "Inconsistent Service State Table, did not find '" <<
625 relatedStateVariableName <<
626 "' for argument '" << argument.GetName() <<
627 "' for action '" << action.GetName() <<
628 "' for service '" << GetServiceType() << "'.";
629 AddDebugLogLineM(false, logUPnP, msg);
630 return false;
632 const CUPnPStateVariable &stateVariable = *(itSVT->second);
633 if ( !stateVariable.GetAllowedValueList().empty() &&
634 stateVariable.GetAllowedValueList().find(ArgValue[i].GetValue()) ==
635 stateVariable.GetAllowedValueList().end()) {
636 msg << "Value not allowed '" << ArgValue[i].GetValue() <<
637 "' for state variable '" << relatedStateVariableName <<
638 "' for argument '" << argument.GetName() <<
639 "' for action '" << action.GetName() <<
640 "' for service '" << GetServiceType() << "'.";
641 AddDebugLogLineM(false, logUPnP, msg);
642 return false;
645 if (firstTime) {
646 firstTime = false;
647 } else {
648 msgAction << ", ";
650 msgAction <<
651 ArgValue[i].GetArgument() <<
652 "='" <<
653 ArgValue[i].GetValue() <<
654 "'";
656 msgAction << ")";
657 AddDebugLogLineM(false, logUPnP, msgAction);
658 // Everything is ok, make the action
659 IXML_Document *ActionDoc = NULL;
660 if (ArgValue.size()) {
661 for (unsigned int i = 0; i < ArgValue.size(); ++i) {
662 int ret = UpnpAddToAction(
663 &ActionDoc,
664 action.GetName().c_str(),
665 GetServiceType().c_str(),
666 ArgValue[i].GetArgument().c_str(),
667 ArgValue[i].GetValue().c_str());
668 if (ret != UPNP_E_SUCCESS) {
669 m_upnpLib.processUPnPErrorMessage(
670 "UpnpAddToAction", ret, NULL, NULL);
671 return false;
674 } else {
675 ActionDoc = UpnpMakeAction(
676 action.GetName().c_str(),
677 GetServiceType().c_str(),
678 0, NULL);
679 if (!ActionDoc) {
680 msg << "Error: UpnpMakeAction returned NULL.";
681 AddLogLineU(false, logUPnP, msg);
682 return false;
685 #if 0
686 // Send the action asynchronously
687 UpnpSendActionAsync(
688 m_UPnPControlPoint.GetUPnPClientHandle(),
689 GetAbsControlURL().c_str(),
690 GetServiceType().c_str(),
691 NULL, ActionDoc,
692 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
693 NULL);
694 return true;
695 #endif
697 // Send the action synchronously
698 IXML_Document *RespDoc = NULL;
699 int ret = UpnpSendAction(
700 m_UPnPControlPoint.GetUPnPClientHandle(),
701 GetAbsControlURL().c_str(),
702 GetServiceType().c_str(),
703 NULL, ActionDoc, &RespDoc);
704 if (ret != UPNP_E_SUCCESS) {
705 m_upnpLib.processUPnPErrorMessage(
706 "UpnpSendAction", ret, NULL, RespDoc);
707 ixmlDocument_free(ActionDoc);
708 ixmlDocument_free(RespDoc);
709 return false;
711 ixmlDocument_free(ActionDoc);
713 // Check the response document
714 m_upnpLib.ProcessActionResponse(
715 RespDoc, action.GetName());
717 // Free the response document
718 ixmlDocument_free(RespDoc);
720 return true;
724 const std::string CUPnPService::GetStateVariable(
725 const std::string &stateVariableName) const
727 std::ostringstream msg;
728 DOMString StVarVal;
729 int ret = UpnpGetServiceVarStatus(
730 m_UPnPControlPoint.GetUPnPClientHandle(),
731 GetAbsControlURL().c_str(),
732 stateVariableName.c_str(),
733 &StVarVal);
734 if (ret != UPNP_E_SUCCESS) {
735 msg << "GetStateVariable(\"" <<
736 stateVariableName <<
737 "\"): in a call to UpnpGetServiceVarStatus";
738 m_upnpLib.processUPnPErrorMessage(
739 msg.str(), ret, StVarVal, NULL);
740 return stdEmptyString;
742 msg << "GetStateVariable: " <<
743 stateVariableName <<
744 "='" <<
745 StVarVal <<
746 "'.";
747 AddDebugLogLineM(false, logUPnP, msg);
748 return stdEmptyString;
752 CUPnPDevice::CUPnPDevice(
753 const CUPnPControlPoint &upnpControlPoint,
754 CUPnPLib &upnpLib,
755 IXML_Element *device,
756 const std::string &URLBase)
758 m_UPnPControlPoint(upnpControlPoint),
759 m_DeviceList(upnpControlPoint, upnpLib, device, URLBase),
760 m_ServiceList(upnpControlPoint, upnpLib, device, URLBase),
761 m_deviceType (upnpLib.Element_GetChildValueByTag(device, "deviceType")),
762 m_friendlyName (upnpLib.Element_GetChildValueByTag(device, "friendlyName")),
763 m_manufacturer (upnpLib.Element_GetChildValueByTag(device, "manufacturer")),
764 m_manufacturerURL (upnpLib.Element_GetChildValueByTag(device, "manufacturerURL")),
765 m_modelDescription (upnpLib.Element_GetChildValueByTag(device, "modelDescription")),
766 m_modelName (upnpLib.Element_GetChildValueByTag(device, "modelName")),
767 m_modelNumber (upnpLib.Element_GetChildValueByTag(device, "modelNumber")),
768 m_modelURL (upnpLib.Element_GetChildValueByTag(device, "modelURL")),
769 m_serialNumber (upnpLib.Element_GetChildValueByTag(device, "serialNumber")),
770 m_UDN (upnpLib.Element_GetChildValueByTag(device, "UDN")),
771 m_UPC (upnpLib.Element_GetChildValueByTag(device, "UPC")),
772 m_presentationURL (upnpLib.Element_GetChildValueByTag(device, "presentationURL"))
774 std::ostringstream msg;
775 int presURLlen = strlen(URLBase.c_str()) +
776 strlen(m_presentationURL.c_str()) + 2;
777 std::vector<char> vpresURL(presURLlen);
778 char* presURL = &vpresURL[0];
779 int errcode = UpnpResolveURL(
780 URLBase.c_str(),
781 m_presentationURL.c_str(),
782 presURL);
783 if (errcode != UPNP_E_SUCCESS) {
784 msg << "Error generating presentationURL from " <<
785 "|" << URLBase << "|" <<
786 m_presentationURL << "|.";
787 AddDebugLogLineM(false, logUPnP, msg);
788 } else {
789 m_presentationURL = presURL;
792 msg.str("");
793 msg << "\n Device: " <<
794 "\n friendlyName: " << m_friendlyName <<
795 "\n deviceType: " << m_deviceType <<
796 "\n manufacturer: " << m_manufacturer <<
797 "\n manufacturerURL: " << m_manufacturerURL <<
798 "\n modelDescription: " << m_modelDescription <<
799 "\n modelName: " << m_modelName <<
800 "\n modelNumber: " << m_modelNumber <<
801 "\n modelURL: " << m_modelURL <<
802 "\n serialNumber: " << m_serialNumber <<
803 "\n UDN: " << m_UDN <<
804 "\n UPC: " << m_UPC <<
805 "\n presentationURL: " << m_presentationURL;
806 AddDebugLogLineM(false, logUPnP, msg);
810 CUPnPRootDevice::CUPnPRootDevice(
811 const CUPnPControlPoint &upnpControlPoint,
812 CUPnPLib &upnpLib,
813 IXML_Element *rootDevice,
814 const std::string &OriginalURLBase,
815 const std::string &FixedURLBase,
816 const char *location,
817 int expires)
819 CUPnPDevice(upnpControlPoint, upnpLib, rootDevice, FixedURLBase),
820 m_UPnPControlPoint(upnpControlPoint),
821 m_URLBase(OriginalURLBase),
822 m_location(location),
823 m_expires(expires)
825 std::ostringstream msg;
826 msg <<
827 "\n Root Device: " <<
828 "\n URLBase: " << m_URLBase <<
829 "\n Fixed URLBase: " << FixedURLBase <<
830 "\n location: " << m_location <<
831 "\n expires: " << m_expires;
832 AddDebugLogLineM(false, logUPnP, msg);
836 CUPnPControlPoint *CUPnPControlPoint::s_CtrlPoint = NULL;
839 CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort)
841 m_upnpLib(*this),
842 m_UPnPClientHandle(),
843 m_RootDeviceMap(),
844 m_ServiceMap(),
845 m_ActivePortMappingsMap(),
846 m_RootDeviceListMutex(),
847 m_IGWDeviceDetected(false),
848 m_WanService(NULL)
850 // Pointer to self
851 s_CtrlPoint = this;
852 // Null string at first
853 std::ostringstream msg;
855 // Start UPnP
856 int ret;
857 char *ipAddress = NULL;
858 unsigned short port = 0;
859 ret = UpnpInit(ipAddress, udpPort);
860 if (ret != UPNP_E_SUCCESS) {
861 msg << "error(UpnpInit): Error code ";
862 goto error;
864 port = UpnpGetServerPort();
865 ipAddress = UpnpGetServerIpAddress();
866 msg << "bound to " << ipAddress << ":" <<
867 port << ".";
868 AddLogLineU(false, logUPnP, msg);
869 msg.str("");
870 ret = UpnpRegisterClient(
871 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
872 &m_UPnPClientHandle,
873 &m_UPnPClientHandle);
874 if (ret != UPNP_E_SUCCESS) {
875 msg << "error(UpnpRegisterClient): Error registering callback: ";
876 goto error;
879 // We could ask for just the right device here. If the root device
880 // contains the device we want, it will respond with the full XML doc,
881 // including the root device and every sub-device it has.
883 // But lets find out what we have in our network by calling UPNP_ROOT_DEVICE.
885 // We should not search twice, because this will produce two
886 // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
887 // on the mutex.
888 ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_ROOT_DEVICE.c_str(), NULL);
889 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this);
890 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this);
891 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this);
892 if (ret != UPNP_E_SUCCESS) {
893 msg << "error(UpnpSearchAsync): Error sending search request: ";
894 goto error;
897 // Wait for the UPnP initialization to complete.
899 // Lock the search timeout mutex
900 m_WaitForSearchTimeoutMutex.Lock();
902 // Lock it again, so that we block. Unlocking will only happen
903 // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
904 // callback.
905 CUPnPMutexLocker lock(m_WaitForSearchTimeoutMutex);
907 return;
909 // Error processing
910 error:
911 UpnpFinish();
912 msg << ret << ": " << m_upnpLib.GetUPnPErrorMessage(ret) << ".";
913 throw CUPnPException(msg);
917 CUPnPControlPoint::~CUPnPControlPoint()
919 for( RootDeviceMap::iterator it = m_RootDeviceMap.begin();
920 it != m_RootDeviceMap.end();
921 ++it) {
922 delete it->second;
924 // Remove all first
925 // RemoveAll();
926 UpnpUnRegisterClient(m_UPnPClientHandle);
927 UpnpFinish();
931 bool CUPnPControlPoint::AddPortMappings(
932 std::vector<CUPnPPortMapping> &upnpPortMapping)
934 std::ostringstream msg;
935 if (!WanServiceDetected()) {
936 msg << "UPnP Error: "
937 "CUPnPControlPoint::AddPortMapping: "
938 "WAN Service not detected.";
939 AddLogLineU(true, logUPnP, msg);
940 return false;
943 int n = upnpPortMapping.size();
944 bool ok = false;
946 // Check the number of port mappings before
947 std::istringstream PortMappingNumberOfEntries(
948 m_WanService->GetStateVariable(
949 "PortMappingNumberOfEntries"));
950 unsigned long oldNumberOfEntries;
951 PortMappingNumberOfEntries >> oldNumberOfEntries;
953 // Add the enabled port mappings
954 for (int i = 0; i < n; ++i) {
955 if (upnpPortMapping[i].getEnabled() == "1") {
956 // Add the mapping to the control point
957 // active mappings list
958 m_ActivePortMappingsMap[upnpPortMapping[i].getKey()] =
959 upnpPortMapping[i];
961 // Add the port mapping
962 PrivateAddPortMapping(upnpPortMapping[i]);
966 // Test some variables, this is deprecated, might not work
967 // with some routers
968 m_WanService->GetStateVariable("ConnectionType");
969 m_WanService->GetStateVariable("PossibleConnectionTypes");
970 m_WanService->GetStateVariable("ConnectionStatus");
971 m_WanService->GetStateVariable("Uptime");
972 m_WanService->GetStateVariable("LastConnectionError");
973 m_WanService->GetStateVariable("RSIPAvailable");
974 m_WanService->GetStateVariable("NATEnabled");
975 m_WanService->GetStateVariable("ExternalIPAddress");
976 m_WanService->GetStateVariable("PortMappingNumberOfEntries");
977 m_WanService->GetStateVariable("PortMappingLeaseDuration");
979 // Just for testing
980 std::vector<CUPnPArgumentValue> argval;
981 argval.resize(0);
982 m_WanService->Execute("GetStatusInfo", argval);
984 #if 0
985 // These do not work. Their value must be requested for a
986 // specific port mapping.
987 m_WanService->GetStateVariable("PortMappingEnabled");
988 m_WanService->GetStateVariable("RemoteHost");
989 m_WanService->GetStateVariable("ExternalPort");
990 m_WanService->GetStateVariable("InternalPort");
991 m_WanService->GetStateVariable("PortMappingProtocol");
992 m_WanService->GetStateVariable("InternalClient");
993 m_WanService->GetStateVariable("PortMappingDescription");
994 #endif
996 // Debug only
997 msg.str("");
998 msg << "CUPnPControlPoint::DeletePortMappings: "
999 "m_ActivePortMappingsMap.size() == " <<
1000 m_ActivePortMappingsMap.size();
1001 AddDebugLogLineM(false, logUPnP, msg);
1003 // Not very good, must find a better test
1004 PortMappingNumberOfEntries.str(
1005 m_WanService->GetStateVariable(
1006 "PortMappingNumberOfEntries"));
1007 unsigned long newNumberOfEntries;
1008 PortMappingNumberOfEntries >> newNumberOfEntries;
1009 ok = newNumberOfEntries - oldNumberOfEntries == 4;
1011 return ok;
1015 void CUPnPControlPoint::RefreshPortMappings()
1017 for ( PortMappingMap::iterator it = m_ActivePortMappingsMap.begin();
1018 it != m_ActivePortMappingsMap.end();
1019 ++it) {
1020 PrivateAddPortMapping(it->second);
1023 // For testing
1024 m_WanService->GetStateVariable("PortMappingNumberOfEntries");
1028 bool CUPnPControlPoint::PrivateAddPortMapping(
1029 CUPnPPortMapping &upnpPortMapping)
1031 // Get an IP address. The UPnP server one must do.
1032 std::string ipAddress(UpnpGetServerIpAddress());
1034 // Start building the action
1035 std::string actionName("AddPortMapping");
1036 std::vector<CUPnPArgumentValue> argval(8);
1038 // Action parameters
1039 argval[0].SetArgument("NewRemoteHost");
1040 argval[0].SetValue("");
1041 argval[1].SetArgument("NewExternalPort");
1042 argval[1].SetValue(upnpPortMapping.getPort());
1043 argval[2].SetArgument("NewProtocol");
1044 argval[2].SetValue(upnpPortMapping.getProtocol());
1045 argval[3].SetArgument("NewInternalPort");
1046 argval[3].SetValue(upnpPortMapping.getPort());
1047 argval[4].SetArgument("NewInternalClient");
1048 argval[4].SetValue(ipAddress);
1049 argval[5].SetArgument("NewEnabled");
1050 argval[5].SetValue("1");
1051 argval[6].SetArgument("NewPortMappingDescription");
1052 argval[6].SetValue(upnpPortMapping.getDescription());
1053 argval[7].SetArgument("NewLeaseDuration");
1054 argval[7].SetValue("0");
1056 // Execute
1057 bool ret = true;
1058 for (ServiceMap::iterator it = m_ServiceMap.begin();
1059 it != m_ServiceMap.end(); ++it) {
1060 ret &= it->second->Execute(actionName, argval);
1063 return ret;
1067 bool CUPnPControlPoint::DeletePortMappings(
1068 std::vector<CUPnPPortMapping> &upnpPortMapping)
1070 std::ostringstream msg;
1071 if (!WanServiceDetected()) {
1072 msg << "UPnP Error: "
1073 "CUPnPControlPoint::DeletePortMapping: "
1074 "WAN Service not detected.";
1075 AddLogLineU(true, logUPnP, msg);
1076 return false;
1079 int n = upnpPortMapping.size();
1080 bool ok = false;
1082 // Check the number of port mappings before
1083 std::istringstream PortMappingNumberOfEntries(
1084 m_WanService->GetStateVariable(
1085 "PortMappingNumberOfEntries"));
1086 unsigned long oldNumberOfEntries;
1087 PortMappingNumberOfEntries >> oldNumberOfEntries;
1089 // Delete the enabled port mappings
1090 for (int i = 0; i < n; ++i) {
1091 if (upnpPortMapping[i].getEnabled() == "1") {
1092 // Delete the mapping from the control point
1093 // active mappings list
1094 PortMappingMap::iterator it =
1095 m_ActivePortMappingsMap.find(
1096 upnpPortMapping[i].getKey());
1097 if (it != m_ActivePortMappingsMap.end()) {
1098 m_ActivePortMappingsMap.erase(it);
1099 } else {
1100 msg << "UPnP Error: "
1101 "CUPnPControlPoint::DeletePortMapping: "
1102 "Mapping was not found in the active "
1103 "mapping map.";
1104 AddLogLineU(true, logUPnP, msg);
1107 // Delete the port mapping
1108 PrivateDeletePortMapping(upnpPortMapping[i]);
1112 // Debug only
1113 msg.str("");
1114 msg << "CUPnPControlPoint::DeletePortMappings: "
1115 "m_ActivePortMappingsMap.size() == " <<
1116 m_ActivePortMappingsMap.size();
1117 AddDebugLogLineM(false, logUPnP, msg);
1119 // Not very good, must find a better test
1120 PortMappingNumberOfEntries.str(
1121 m_WanService->GetStateVariable(
1122 "PortMappingNumberOfEntries"));
1123 unsigned long newNumberOfEntries;
1124 PortMappingNumberOfEntries >> newNumberOfEntries;
1125 ok = oldNumberOfEntries - newNumberOfEntries == 4;
1127 return ok;
1131 bool CUPnPControlPoint::PrivateDeletePortMapping(
1132 CUPnPPortMapping &upnpPortMapping)
1134 // Start building the action
1135 std::string actionName("DeletePortMapping");
1136 std::vector<CUPnPArgumentValue> argval(3);
1138 // Action parameters
1139 argval[0].SetArgument("NewRemoteHost");
1140 argval[0].SetValue("");
1141 argval[1].SetArgument("NewExternalPort");
1142 argval[1].SetValue(upnpPortMapping.getPort());
1143 argval[2].SetArgument("NewProtocol");
1144 argval[2].SetValue(upnpPortMapping.getProtocol());
1146 // Execute
1147 bool ret = true;
1148 for (ServiceMap::iterator it = m_ServiceMap.begin();
1149 it != m_ServiceMap.end(); ++it) {
1150 ret &= it->second->Execute(actionName, argval);
1153 return ret;
1157 // This function is static
1158 int CUPnPControlPoint::Callback(Upnp_EventType EventType, void *Event, void * /*Cookie*/)
1160 std::ostringstream msg;
1161 std::ostringstream msg2;
1162 // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
1163 // happen with a wrong cookie and... boom!
1164 // CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
1165 CUPnPControlPoint *upnpCP = CUPnPControlPoint::s_CtrlPoint;
1167 //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
1168 switch (EventType) {
1169 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
1170 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
1171 msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): ";
1172 msg2<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
1173 goto upnpDiscovery;
1174 case UPNP_DISCOVERY_SEARCH_RESULT: {
1175 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n");
1176 msg << "error(UPNP_DISCOVERY_SEARCH_RESULT): ";
1177 msg2<< "UPNP_DISCOVERY_SEARCH_RESULT: ";
1178 // UPnP Discovery
1179 upnpDiscovery:
1180 struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
1181 IXML_Document *doc = NULL;
1182 int ret;
1183 if (d_event->ErrCode != UPNP_E_SUCCESS) {
1184 msg << upnpCP->m_upnpLib.GetUPnPErrorMessage(d_event->ErrCode) << ".";
1185 AddDebugLogLineM(true, logUPnP, msg);
1187 // Get the XML tree device description in doc
1188 ret = UpnpDownloadXmlDoc(d_event->Location, &doc);
1189 if (ret != UPNP_E_SUCCESS) {
1190 msg << "Error retrieving device description from " <<
1191 d_event->Location << ": " <<
1192 upnpCP->m_upnpLib.GetUPnPErrorMessage(ret) << ".";
1193 AddDebugLogLineM(true, logUPnP, msg);
1194 } else {
1195 msg2 << "Retrieving device description from " <<
1196 d_event->Location << ".";
1197 AddDebugLogLineM(false, logUPnP, msg2);
1199 if (doc) {
1200 // Get the root node
1201 IXML_Element *root =
1202 upnpCP->m_upnpLib.Element_GetRootElement(doc);
1203 // Extract the URLBase
1204 const std::string urlBase = upnpCP->m_upnpLib.
1205 Element_GetChildValueByTag(root, "URLBase");
1206 // Get the root device
1207 IXML_Element *rootDevice = upnpCP->m_upnpLib.
1208 Element_GetFirstChildByTag(root, "device");
1209 // Extract the deviceType
1210 std::string devType(upnpCP->m_upnpLib.
1211 Element_GetChildValueByTag(rootDevice, "deviceType"));
1212 // Only add device if it is an InternetGatewayDevice
1213 if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) {
1214 // This condition can be used to auto-detect
1215 // the UPnP device we are interested in.
1216 // Obs.: Don't block the entry here on this
1217 // condition! There may be more than one device,
1218 // and the first that enters may not be the one
1219 // we are interested in!
1220 upnpCP->SetIGWDeviceDetected(true);
1221 // Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
1222 // we don't want to spam our logs.
1223 if (EventType != UPNP_DISCOVERY_ADVERTISEMENT_ALIVE) {
1224 msg.str("Internet Gateway Device Detected.");
1225 AddLogLineU(true, logUPnP, msg);
1227 // Add the root device to our list
1228 upnpCP->AddRootDevice(rootDevice, urlBase,
1229 d_event->Location, d_event->Expires);
1231 // Free the XML doc tree
1232 ixmlDocument_free(doc);
1234 break;
1236 case UPNP_DISCOVERY_SEARCH_TIMEOUT: {
1237 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1238 // Search timeout
1239 msg << "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1240 AddDebugLogLineM(false, logUPnP, msg);
1242 // Unlock the search timeout mutex
1243 upnpCP->m_WaitForSearchTimeoutMutex.Unlock();
1245 break;
1247 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: {
1248 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
1249 // UPnP Device Removed
1250 struct Upnp_Discovery *dab_event = (struct Upnp_Discovery *)Event;
1251 if (dab_event->ErrCode != UPNP_E_SUCCESS) {
1252 msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
1253 upnpCP->m_upnpLib.GetUPnPErrorMessage(dab_event->ErrCode) <<
1254 ".";
1255 AddDebugLogLineM(true, logUPnP, msg);
1257 std::string devType = dab_event->DeviceType;
1258 // Check for an InternetGatewayDevice and removes it from the list
1259 std::transform(devType.begin(), devType.end(), devType.begin(), tolower);
1260 if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) {
1261 upnpCP->RemoveRootDevice(dab_event->DeviceId);
1263 break;
1265 case UPNP_EVENT_RECEIVED: {
1266 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1267 // Event reveived
1268 struct Upnp_Event *e_event = (struct Upnp_Event *)Event;
1269 const std::string Sid = e_event->Sid;
1270 // Parses the event
1271 upnpCP->OnEventReceived(Sid, e_event->EventKey, e_event->ChangedVariables);
1272 break;
1274 case UPNP_EVENT_SUBSCRIBE_COMPLETE:
1275 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
1276 msg << "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): ";
1277 goto upnpEventRenewalComplete;
1278 case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
1279 //fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
1280 msg << "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): ";
1281 goto upnpEventRenewalComplete;
1282 case UPNP_EVENT_RENEWAL_COMPLETE: {
1283 //fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n");
1284 msg << "error(UPNP_EVENT_RENEWAL_COMPLETE): ";
1285 upnpEventRenewalComplete:
1286 struct Upnp_Event_Subscribe *es_event =
1287 (struct Upnp_Event_Subscribe *)Event;
1288 if (es_event->ErrCode != UPNP_E_SUCCESS) {
1289 msg << "Error in Event Subscribe Callback";
1290 upnpCP->m_upnpLib.processUPnPErrorMessage(
1291 msg.str(), es_event->ErrCode, NULL, NULL);
1292 } else {
1293 #if 0
1294 TvCtrlPointHandleSubscribeUpdate(
1295 es_event->PublisherUrl,
1296 es_event->Sid,
1297 es_event->TimeOut );
1298 #endif
1301 break;
1304 case UPNP_EVENT_AUTORENEWAL_FAILED:
1305 //fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n");
1306 msg << "error(UPNP_EVENT_AUTORENEWAL_FAILED): ";
1307 msg2 << "UPNP_EVENT_AUTORENEWAL_FAILED: ";
1308 goto upnpEventSubscriptionExpired;
1309 case UPNP_EVENT_SUBSCRIPTION_EXPIRED: {
1310 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
1311 msg << "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): ";
1312 msg2 << "UPNP_EVENT_SUBSCRIPTION_EXPIRED: ";
1313 upnpEventSubscriptionExpired:
1314 struct Upnp_Event_Subscribe *es_event =
1315 (struct Upnp_Event_Subscribe *)Event;
1316 Upnp_SID newSID;
1317 int TimeOut = 1801;
1318 int ret = UpnpSubscribe(
1319 upnpCP->m_UPnPClientHandle,
1320 es_event->PublisherUrl,
1321 &TimeOut,
1322 newSID);
1323 if (ret != UPNP_E_SUCCESS) {
1324 msg << "Error Subscribing to EventURL";
1325 upnpCP->m_upnpLib.processUPnPErrorMessage(
1326 msg.str(), es_event->ErrCode, NULL, NULL);
1327 } else {
1328 ServiceMap::iterator it =
1329 upnpCP->m_ServiceMap.find(es_event->PublisherUrl);
1330 if (it != upnpCP->m_ServiceMap.end()) {
1331 CUPnPService &service = *(it->second);
1332 service.SetTimeout(TimeOut);
1333 service.SetSID(newSID);
1334 msg2 << "Re-subscribed to EventURL '" <<
1335 es_event->PublisherUrl <<
1336 "' with SID == '" <<
1337 newSID << "'.";
1338 AddDebugLogLineM(true, logUPnP, msg2);
1339 // In principle, we should test to see if the
1340 // service is the same. But here we only have one
1341 // service, so...
1342 upnpCP->RefreshPortMappings();
1343 } else {
1344 msg << "Error: did not find service " <<
1345 newSID << " in the service map.";
1346 AddDebugLogLineM(true, logUPnP, msg);
1349 break;
1351 case UPNP_CONTROL_ACTION_COMPLETE: {
1352 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n");
1353 // This is here if we choose to do this asynchronously
1354 struct Upnp_Action_Complete *a_event =
1355 (struct Upnp_Action_Complete *)Event;
1356 if (a_event->ErrCode != UPNP_E_SUCCESS) {
1357 upnpCP->m_upnpLib.processUPnPErrorMessage(
1358 "UpnpSendActionAsync",
1359 a_event->ErrCode, NULL,
1360 a_event->ActionResult);
1361 } else {
1362 // Check the response document
1363 upnpCP->m_upnpLib.ProcessActionResponse(
1364 a_event->ActionResult,
1365 "<UpnpSendActionAsync>");
1367 /* No need for any processing here, just print out results.
1368 * Service state table updates are handled by events.
1370 break;
1372 case UPNP_CONTROL_GET_VAR_COMPLETE: {
1373 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n");
1374 msg << "error(UPNP_CONTROL_GET_VAR_COMPLETE): ";
1375 struct Upnp_State_Var_Complete *sv_event =
1376 (struct Upnp_State_Var_Complete *)Event;
1377 if (sv_event->ErrCode != UPNP_E_SUCCESS) {
1378 msg << "m_UpnpGetServiceVarStatusAsync";
1379 upnpCP->m_upnpLib.processUPnPErrorMessage(
1380 msg.str(), sv_event->ErrCode, NULL, NULL);
1381 } else {
1382 #if 0
1383 // Warning: The use of UpnpGetServiceVarStatus and
1384 // UpnpGetServiceVarStatusAsync is deprecated by the
1385 // UPnP forum.
1386 TvCtrlPointHandleGetVar(
1387 sv_event->CtrlUrl,
1388 sv_event->StateVarName,
1389 sv_event->CurrentVal );
1390 #endif
1392 break;
1394 // ignore these cases, since this is not a device
1395 case UPNP_CONTROL_GET_VAR_REQUEST:
1396 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n");
1397 msg << "error(UPNP_CONTROL_GET_VAR_REQUEST): ";
1398 goto eventSubscriptionRequest;
1399 case UPNP_CONTROL_ACTION_REQUEST:
1400 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n");
1401 msg << "error(UPNP_CONTROL_ACTION_REQUEST): ";
1402 goto eventSubscriptionRequest;
1403 case UPNP_EVENT_SUBSCRIPTION_REQUEST:
1404 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
1405 msg << "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): ";
1406 eventSubscriptionRequest:
1407 msg << "This is not a UPnP Device, this is a UPnP Control Point, event ignored.";
1408 AddDebugLogLineM(true, logUPnP, msg);
1409 break;
1410 default:
1411 // Humm, this is not good, we forgot to handle something...
1412 fprintf(stderr,
1413 "Callback: default... Unknown event:'%d', not good.\n",
1414 EventType);
1415 msg << "error(UPnP::Callback): Event not handled:'" <<
1416 EventType << "'.";
1417 fprintf(stderr, "%s\n", msg.str().c_str());
1418 AddDebugLogLineM(true, logUPnP, msg);
1419 // Better not throw in the callback. Who would catch it?
1420 //throw CUPnPException(msg);
1421 break;
1424 return 0;
1428 void CUPnPControlPoint::OnEventReceived(
1429 const std::string &Sid,
1430 int EventKey,
1431 IXML_Document *ChangedVariablesDoc)
1433 std::ostringstream msg;
1434 msg << "UPNP_EVENT_RECEIVED:" <<
1435 "\n SID: " << Sid <<
1436 "\n Key: " << EventKey <<
1437 "\n Property list:";
1438 IXML_Element *root =
1439 m_upnpLib.Element_GetRootElement(ChangedVariablesDoc);
1440 IXML_Element *child =
1441 m_upnpLib.Element_GetFirstChild(root);
1442 if (child) {
1443 while (child) {
1444 IXML_Element *child2 =
1445 m_upnpLib.Element_GetFirstChild(child);
1446 const DOMString childTag =
1447 m_upnpLib.Element_GetTag(child2);
1448 std::string childValue =
1449 m_upnpLib.Element_GetTextValue(child2);
1450 msg << "\n " <<
1451 childTag << "='" <<
1452 childValue << "'";
1453 child = m_upnpLib.Element_GetNextSibling(child);
1455 } else {
1456 msg << "\n Empty property list.";
1458 AddDebugLogLineM(true, logUPnP, msg);
1459 // Freeing that doc segfaults. Probably should not be freed.
1460 //ixmlDocument_free(ChangedVariablesDoc);
1464 void CUPnPControlPoint::AddRootDevice(
1465 IXML_Element *rootDevice, const std::string &urlBase,
1466 const char *location, int expires)
1468 // Lock the Root Device List
1469 CUPnPMutexLocker lock(m_RootDeviceListMutex);
1471 // Root node's URLBase
1472 std::string OriginalURLBase(urlBase);
1473 std::string FixedURLBase(OriginalURLBase.empty() ?
1474 location :
1475 OriginalURLBase);
1477 // Get the UDN (Unique Device Name)
1478 std::string UDN(
1479 m_upnpLib.Element_GetChildValueByTag(rootDevice, "UDN"));
1480 RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
1481 bool alreadyAdded = it != m_RootDeviceMap.end();
1482 if (alreadyAdded) {
1483 // Just set the expires field
1484 it->second->SetExpires(expires);
1485 } else {
1486 // Add a new root device to the root device list
1487 CUPnPRootDevice *upnpRootDevice = new CUPnPRootDevice(
1488 *this, m_upnpLib, rootDevice,
1489 OriginalURLBase, FixedURLBase,
1490 location, expires);
1491 m_RootDeviceMap[upnpRootDevice->GetUDN()] = upnpRootDevice;
1496 void CUPnPControlPoint::RemoveRootDevice(const char *udn)
1498 // Lock the Root Device List
1499 CUPnPMutexLocker lock(m_RootDeviceListMutex);
1501 // Remove
1502 std::string UDN(udn);
1503 RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
1504 if (it != m_RootDeviceMap.end()) {
1505 delete it->second;
1506 m_RootDeviceMap.erase(UDN);
1511 void CUPnPControlPoint::Subscribe(CUPnPService &service)
1513 std::ostringstream msg;
1515 IXML_Document *scpdDoc = NULL;
1516 int errcode = UpnpDownloadXmlDoc(
1517 service.GetAbsSCPDURL().c_str(), &scpdDoc);
1518 if (errcode == UPNP_E_SUCCESS) {
1519 // Get the root node of this service (the SCPD Document)
1520 IXML_Element *scpdRoot =
1521 m_upnpLib.Element_GetRootElement(scpdDoc);
1522 CUPnPSCPD *scpd = new CUPnPSCPD(*this, m_upnpLib,
1523 scpdRoot, service.GetAbsSCPDURL());
1524 service.SetSCPD(scpd);
1525 m_ServiceMap[service.GetAbsEventSubURL()] = &service;
1526 msg << "Successfully retrieved SCPD Document for service " <<
1527 service.GetServiceType() << ", absEventSubURL: " <<
1528 service.GetAbsEventSubURL() << ".";
1529 AddLogLineU(true, logUPnP, msg);
1530 msg.str("");
1532 // Now try to subscribe to this service. If the subscription
1533 // is not successfull, we will not be notified about events,
1534 // but it may be possible to use the service anyway.
1535 errcode = UpnpSubscribe(m_UPnPClientHandle,
1536 service.GetAbsEventSubURL().c_str(),
1537 service.GetTimeoutAddr(),
1538 service.GetSID());
1539 if (errcode == UPNP_E_SUCCESS) {
1540 msg << "Successfully subscribed to service " <<
1541 service.GetServiceType() << ", absEventSubURL: " <<
1542 service.GetAbsEventSubURL() << ".";
1543 AddLogLineU(true, logUPnP, msg);
1544 } else {
1545 msg << "Error subscribing to service " <<
1546 service.GetServiceType() << ", absEventSubURL: " <<
1547 service.GetAbsEventSubURL() << ", error: " <<
1548 m_upnpLib.GetUPnPErrorMessage(errcode) << ".";
1549 goto error;
1551 } else {
1552 msg << "Error getting SCPD Document from " <<
1553 service.GetAbsSCPDURL() << ".";
1554 AddLogLineU(true, logUPnP, msg);
1557 return;
1559 // Error processing
1560 error:
1561 AddLogLineU(true, logUPnP, msg);
1565 void CUPnPControlPoint::Unsubscribe(CUPnPService &service)
1567 ServiceMap::iterator it = m_ServiceMap.find(service.GetAbsEventSubURL());
1568 if (it != m_ServiceMap.end()) {
1569 m_ServiceMap.erase(it);
1570 UpnpUnSubscribe(m_UPnPClientHandle, service.GetSID());
1575 // File_checked_for_headers