Upstream tarball 20080708
[amule.git] / src / UPnPBase.cpp
blob6339050c260ce9c16b27ab81ec9efdc82b80b5b6
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 AddLogLineM(false, logUPnP, msg);
140 } else {
141 msg << "Error: " <<
142 messsage <<
143 ": UPnP SDK error: " <<
144 GetUPnPErrorMessage(errorCode) <<
145 " (" << errorCode << ").";
146 AddLogLineM(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 m_serviceType == upnpLib.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG ||
529 m_serviceType == upnpLib.UPNP_SERVICE_LAYER3_FORWARDING) {
530 //#warning Delete this code on release.
531 //if (!upnpLib.m_ctrlPoint.WanServiceDetected()) {
532 // This condition can be used to suspend the parse
533 // of the XML tree.
534 //#warning Delete this code when m_WanService is no longer used.
535 upnpLib.m_ctrlPoint.SetWanService(this);
536 // Log it
537 msg.str("");
538 msg << "WAN Service Detected: '" <<
539 m_serviceType << "'.";
540 AddDebugLogLineM(true, logUPnP, msg);
541 // Subscribe
542 upnpLib.m_ctrlPoint.Subscribe(*this);
543 //#warning Delete this code on release.
544 #if 0
545 } else {
546 msg.str("");
547 msg << "WAN service detected again: '" <<
548 m_serviceType <<
549 "'. Will only use the first instance.";
550 AddDebugLogLineM(true, logUPnP, msg);
552 #endif
553 } else {
554 msg.str("");
555 msg << "Uninteresting service detected: '" <<
556 m_serviceType << "'. Ignoring.";
557 AddDebugLogLineM(true, logUPnP, msg);
562 CUPnPService::~CUPnPService()
567 bool CUPnPService::Execute(
568 const std::string &ActionName,
569 const std::vector<CUPnPArgumentValue> &ArgValue) const
571 std::ostringstream msg;
572 std::ostringstream msgAction("Sending action ");
573 // Check for correct action name
574 ActionList::const_iterator itAction =
575 m_SCPD->GetActionList().find(ActionName);
576 if (itAction == m_SCPD->GetActionList().end()) {
577 msg << "Invalid action name '" << ActionName <<
578 "' for service '" << GetServiceType() << "'.";
579 AddDebugLogLineM(false, logUPnP, msg);
580 return false;
582 msgAction << ActionName << "(";
583 bool firstTime = true;
584 // Check for correct Argument/Value pairs
585 const CUPnPAction &action = *(itAction->second);
586 for (unsigned int i = 0; i < ArgValue.size(); ++i) {
587 ArgumentList::const_iterator itArg =
588 action.GetArgumentList().find(ArgValue[i].GetArgument());
589 if (itArg == action.GetArgumentList().end()) {
590 msg << "Invalid argument name '" << ArgValue[i].GetArgument() <<
591 "' for action '" << action.GetName() <<
592 "' for service '" << GetServiceType() << "'.";
593 AddDebugLogLineM(false, logUPnP, msg);
594 return false;
596 const CUPnPArgument &argument = *(itArg->second);
597 if (tolower(argument.GetDirection()[0]) != 'i' ||
598 tolower(argument.GetDirection()[1]) != 'n') {
599 msg << "Invalid direction for argument '" <<
600 ArgValue[i].GetArgument() <<
601 "' for action '" << action.GetName() <<
602 "' for service '" << GetServiceType() << "'.";
603 AddDebugLogLineM(false, logUPnP, msg);
604 return false;
606 const std::string relatedStateVariableName =
607 argument.GetRelatedStateVariable();
608 if (!relatedStateVariableName.empty()) {
609 ServiceStateTable::const_iterator itSVT =
610 m_SCPD->GetServiceStateTable().
611 find(relatedStateVariableName);
612 if (itSVT == m_SCPD->GetServiceStateTable().end()) {
613 msg << "Inconsistent Service State Table, did not find '" <<
614 relatedStateVariableName <<
615 "' for argument '" << argument.GetName() <<
616 "' for action '" << action.GetName() <<
617 "' for service '" << GetServiceType() << "'.";
618 AddDebugLogLineM(false, logUPnP, msg);
619 return false;
621 const CUPnPStateVariable &stateVariable = *(itSVT->second);
622 if ( !stateVariable.GetAllowedValueList().empty() &&
623 stateVariable.GetAllowedValueList().find(ArgValue[i].GetValue()) ==
624 stateVariable.GetAllowedValueList().end()) {
625 msg << "Value not allowed '" << ArgValue[i].GetValue() <<
626 "' for state variable '" << relatedStateVariableName <<
627 "' for argument '" << argument.GetName() <<
628 "' for action '" << action.GetName() <<
629 "' for service '" << GetServiceType() << "'.";
630 AddDebugLogLineM(false, logUPnP, msg);
631 return false;
634 if (firstTime) {
635 firstTime = false;
636 } else {
637 msgAction << ", ";
639 msgAction <<
640 ArgValue[i].GetArgument() <<
641 "='" <<
642 ArgValue[i].GetValue() <<
643 "'";
645 msgAction << ")";
646 AddDebugLogLineM(false, logUPnP, msgAction);
647 // Everything is ok, make the action
648 IXML_Document *ActionDoc = NULL;
649 if (ArgValue.size()) {
650 for (unsigned int i = 0; i < ArgValue.size(); ++i) {
651 int ret = UpnpAddToAction(
652 &ActionDoc,
653 action.GetName().c_str(),
654 GetServiceType().c_str(),
655 ArgValue[i].GetArgument().c_str(),
656 ArgValue[i].GetValue().c_str());
657 if (ret != UPNP_E_SUCCESS) {
658 m_upnpLib.processUPnPErrorMessage(
659 "UpnpAddToAction", ret, NULL, NULL);
660 return false;
663 } else {
664 ActionDoc = UpnpMakeAction(
665 action.GetName().c_str(),
666 GetServiceType().c_str(),
667 0, NULL);
668 if (!ActionDoc) {
669 msg << "Error: UpnpMakeAction returned NULL.";
670 AddLogLineM(false, logUPnP, msg);
671 return false;
674 #if 0
675 // Send the action asynchronously
676 UpnpSendActionAsync(
677 m_UPnPControlPoint.GetUPnPClientHandle(),
678 GetAbsControlURL().c_str(),
679 GetServiceType().c_str(),
680 NULL, ActionDoc,
681 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
682 NULL);
683 return true;
684 #endif
686 // Send the action synchronously
687 IXML_Document *RespDoc = NULL;
688 int ret = UpnpSendAction(
689 m_UPnPControlPoint.GetUPnPClientHandle(),
690 GetAbsControlURL().c_str(),
691 GetServiceType().c_str(),
692 NULL, ActionDoc, &RespDoc);
693 if (ret != UPNP_E_SUCCESS) {
694 m_upnpLib.processUPnPErrorMessage(
695 "UpnpSendAction", ret, NULL, RespDoc);
696 ixmlDocument_free(ActionDoc);
697 ixmlDocument_free(RespDoc);
698 return false;
700 ixmlDocument_free(ActionDoc);
702 // Check the response document
703 m_upnpLib.ProcessActionResponse(
704 RespDoc, action.GetName());
706 // Free the response document
707 ixmlDocument_free(RespDoc);
709 return true;
713 const std::string CUPnPService::GetStateVariable(
714 const std::string &stateVariableName) const
716 std::ostringstream msg;
717 DOMString StVarVal;
718 int ret = UpnpGetServiceVarStatus(
719 m_UPnPControlPoint.GetUPnPClientHandle(),
720 GetAbsControlURL().c_str(),
721 stateVariableName.c_str(),
722 &StVarVal);
723 if (ret != UPNP_E_SUCCESS) {
724 msg << "GetStateVariable(\"" <<
725 stateVariableName <<
726 "\"): in a call to UpnpGetServiceVarStatus";
727 m_upnpLib.processUPnPErrorMessage(
728 msg.str(), ret, StVarVal, NULL);
729 return stdEmptyString;
731 msg << "GetStateVariable: " <<
732 stateVariableName <<
733 "='" <<
734 StVarVal <<
735 "'.";
736 AddDebugLogLineM(false, logUPnP, msg);
737 return stdEmptyString;
741 CUPnPDevice::CUPnPDevice(
742 const CUPnPControlPoint &upnpControlPoint,
743 CUPnPLib &upnpLib,
744 IXML_Element *device,
745 const std::string &URLBase)
747 m_UPnPControlPoint(upnpControlPoint),
748 m_DeviceList(upnpControlPoint, upnpLib, device, URLBase),
749 m_ServiceList(upnpControlPoint, upnpLib, device, URLBase),
750 m_deviceType (upnpLib.Element_GetChildValueByTag(device, "deviceType")),
751 m_friendlyName (upnpLib.Element_GetChildValueByTag(device, "friendlyName")),
752 m_manufacturer (upnpLib.Element_GetChildValueByTag(device, "manufacturer")),
753 m_manufacturerURL (upnpLib.Element_GetChildValueByTag(device, "manufacturerURL")),
754 m_modelDescription (upnpLib.Element_GetChildValueByTag(device, "modelDescription")),
755 m_modelName (upnpLib.Element_GetChildValueByTag(device, "modelName")),
756 m_modelNumber (upnpLib.Element_GetChildValueByTag(device, "modelNumber")),
757 m_modelURL (upnpLib.Element_GetChildValueByTag(device, "modelURL")),
758 m_serialNumber (upnpLib.Element_GetChildValueByTag(device, "serialNumber")),
759 m_UDN (upnpLib.Element_GetChildValueByTag(device, "UDN")),
760 m_UPC (upnpLib.Element_GetChildValueByTag(device, "UPC")),
761 m_presentationURL (upnpLib.Element_GetChildValueByTag(device, "presentationURL"))
763 std::ostringstream msg;
764 int presURLlen = strlen(URLBase.c_str()) +
765 strlen(m_presentationURL.c_str()) + 2;
766 std::vector<char> vpresURL(presURLlen);
767 char* presURL = &vpresURL[0];
768 int errcode = UpnpResolveURL(
769 URLBase.c_str(),
770 m_presentationURL.c_str(),
771 presURL);
772 if (errcode != UPNP_E_SUCCESS) {
773 msg << "Error generating presentationURL from " <<
774 "|" << URLBase << "|" <<
775 m_presentationURL << "|.";
776 AddDebugLogLineM(false, logUPnP, msg);
777 } else {
778 m_presentationURL = presURL;
781 msg.str("");
782 msg << "\n Device: " <<
783 "\n friendlyName: " << m_friendlyName <<
784 "\n deviceType: " << m_deviceType <<
785 "\n manufacturer: " << m_manufacturer <<
786 "\n manufacturerURL: " << m_manufacturerURL <<
787 "\n modelDescription: " << m_modelDescription <<
788 "\n modelName: " << m_modelName <<
789 "\n modelNumber: " << m_modelNumber <<
790 "\n modelURL: " << m_modelURL <<
791 "\n serialNumber: " << m_serialNumber <<
792 "\n UDN: " << m_UDN <<
793 "\n UPC: " << m_UPC <<
794 "\n presentationURL: " << m_presentationURL;
795 AddDebugLogLineM(false, logUPnP, msg);
799 CUPnPRootDevice::CUPnPRootDevice(
800 const CUPnPControlPoint &upnpControlPoint,
801 CUPnPLib &upnpLib,
802 IXML_Element *rootDevice,
803 const std::string &OriginalURLBase,
804 const std::string &FixedURLBase,
805 const char *location,
806 int expires)
808 CUPnPDevice(upnpControlPoint, upnpLib, rootDevice, FixedURLBase),
809 m_UPnPControlPoint(upnpControlPoint),
810 m_URLBase(OriginalURLBase),
811 m_location(location),
812 m_expires(expires)
814 std::ostringstream msg;
815 msg <<
816 "\n Root Device: " <<
817 "\n URLBase: " << m_URLBase <<
818 "\n Fixed URLBase: " << FixedURLBase <<
819 "\n location: " << m_location <<
820 "\n expires: " << m_expires;
821 AddDebugLogLineM(false, logUPnP, msg);
825 CUPnPControlPoint *CUPnPControlPoint::s_CtrlPoint = NULL;
828 CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort)
830 m_upnpLib(*this),
831 m_UPnPClientHandle(),
832 m_RootDeviceMap(),
833 m_ServiceMap(),
834 m_ActivePortMappingsMap(),
835 m_RootDeviceListMutex(),
836 m_IGWDeviceDetected(false),
837 m_WanService(NULL)
839 // Pointer to self
840 s_CtrlPoint = this;
841 // Null string at first
842 std::ostringstream msg;
844 // Start UPnP
845 int ret;
846 char *ipAddress = NULL;
847 unsigned short port = 0;
848 ret = UpnpInit(ipAddress, udpPort);
849 if (ret != UPNP_E_SUCCESS) {
850 msg << "error(UpnpInit): Error code ";
851 goto error;
853 port = UpnpGetServerPort();
854 ipAddress = UpnpGetServerIpAddress();
855 msg << "bound to " << ipAddress << ":" <<
856 port << ".";
857 AddLogLineM(false, logUPnP, msg);
858 msg.str("");
859 ret = UpnpRegisterClient(
860 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
861 &m_UPnPClientHandle,
862 &m_UPnPClientHandle);
863 if (ret != UPNP_E_SUCCESS) {
864 msg << "error(UpnpRegisterClient): Error registering callback: ";
865 goto error;
868 // We could ask for just the right device here. If the root device
869 // contains the device we want, it will respond with the full XML doc,
870 // including the root device and every sub-device it has.
872 // But lets find out what we have in our network by calling UPNP_ROOT_DEVICE.
874 // We should not search twice, because this will produce two
875 // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
876 // on the mutex.
877 ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_ROOT_DEVICE.c_str(), NULL);
878 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this);
879 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this);
880 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this);
881 if (ret != UPNP_E_SUCCESS) {
882 msg << "error(UpnpSearchAsync): Error sending search request: ";
883 goto error;
886 // Wait for the UPnP initialization to complete.
888 // Lock the search timeout mutex
889 m_WaitForSearchTimeoutMutex.Lock();
891 // Lock it again, so that we block. Unlocking will only happen
892 // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
893 // callback.
894 CUPnPMutexLocker lock(m_WaitForSearchTimeoutMutex);
896 return;
898 // Error processing
899 error:
900 UpnpFinish();
901 msg << ret << ": " << m_upnpLib.GetUPnPErrorMessage(ret) << ".";
902 throw CUPnPException(msg);
906 CUPnPControlPoint::~CUPnPControlPoint()
908 for( RootDeviceMap::iterator it = m_RootDeviceMap.begin();
909 it != m_RootDeviceMap.end();
910 ++it) {
911 delete it->second;
913 // Remove all first
914 // RemoveAll();
915 UpnpUnRegisterClient(m_UPnPClientHandle);
916 UpnpFinish();
920 bool CUPnPControlPoint::AddPortMappings(
921 std::vector<CUPnPPortMapping> &upnpPortMapping)
923 std::ostringstream msg;
924 if (!WanServiceDetected()) {
925 msg << "UPnP Error: "
926 "CUPnPControlPoint::AddPortMapping: "
927 "Wan Service not detected.";
928 AddLogLineM(true, logUPnP, msg);
929 return false;
932 int n = upnpPortMapping.size();
933 bool ok = false;
935 // Check the number of port mappings before
936 std::istringstream PortMappingNumberOfEntries(
937 m_WanService->GetStateVariable(
938 "PortMappingNumberOfEntries"));
939 unsigned long oldNumberOfEntries;
940 PortMappingNumberOfEntries >> oldNumberOfEntries;
942 // Add the enabled port mappings
943 for (int i = 0; i < n; ++i) {
944 if (upnpPortMapping[i].getEnabled() == "1") {
945 // Add the mapping to the control point
946 // active mappings list
947 m_ActivePortMappingsMap[upnpPortMapping[i].getKey()] =
948 upnpPortMapping[i];
950 // Add the port mapping
951 PrivateAddPortMapping(upnpPortMapping[i]);
955 // Test some variables, this is deprecated, might not work
956 // with some routers
957 m_WanService->GetStateVariable("ConnectionType");
958 m_WanService->GetStateVariable("PossibleConnectionTypes");
959 m_WanService->GetStateVariable("ConnectionStatus");
960 m_WanService->GetStateVariable("Uptime");
961 m_WanService->GetStateVariable("LastConnectionError");
962 m_WanService->GetStateVariable("RSIPAvailable");
963 m_WanService->GetStateVariable("NATEnabled");
964 m_WanService->GetStateVariable("ExternalIPAddress");
965 m_WanService->GetStateVariable("PortMappingNumberOfEntries");
966 m_WanService->GetStateVariable("PortMappingLeaseDuration");
968 // Just for testing
969 std::vector<CUPnPArgumentValue> argval;
970 argval.resize(0);
971 m_WanService->Execute("GetStatusInfo", argval);
973 #if 0
974 // These do not work. Their value must be requested for a
975 // specific port mapping.
976 m_WanService->GetStateVariable("PortMappingEnabled");
977 m_WanService->GetStateVariable("RemoteHost");
978 m_WanService->GetStateVariable("ExternalPort");
979 m_WanService->GetStateVariable("InternalPort");
980 m_WanService->GetStateVariable("PortMappingProtocol");
981 m_WanService->GetStateVariable("InternalClient");
982 m_WanService->GetStateVariable("PortMappingDescription");
983 #endif
985 // Debug only
986 msg.str("");
987 msg << "CUPnPControlPoint::DeletePortMappings: "
988 "m_ActivePortMappingsMap.size() == " <<
989 m_ActivePortMappingsMap.size();
990 AddDebugLogLineM(false, logUPnP, msg);
992 // Not very good, must find a better test
993 PortMappingNumberOfEntries.str(
994 m_WanService->GetStateVariable(
995 "PortMappingNumberOfEntries"));
996 unsigned long newNumberOfEntries;
997 PortMappingNumberOfEntries >> newNumberOfEntries;
998 ok = newNumberOfEntries - oldNumberOfEntries == 4;
1000 return ok;
1004 void CUPnPControlPoint::RefreshPortMappings()
1006 for ( PortMappingMap::iterator it = m_ActivePortMappingsMap.begin();
1007 it != m_ActivePortMappingsMap.end();
1008 ++it) {
1009 PrivateAddPortMapping(it->second);
1012 // For testing
1013 m_WanService->GetStateVariable("PortMappingNumberOfEntries");
1017 bool CUPnPControlPoint::PrivateAddPortMapping(
1018 CUPnPPortMapping &upnpPortMapping)
1020 // Get an IP address. The UPnP server one must do.
1021 std::string ipAddress(UpnpGetServerIpAddress());
1023 // Start building the action
1024 std::string actionName("AddPortMapping");
1025 std::vector<CUPnPArgumentValue> argval(8);
1027 // Action parameters
1028 argval[0].SetArgument("NewRemoteHost");
1029 argval[0].SetValue("");
1030 argval[1].SetArgument("NewExternalPort");
1031 argval[1].SetValue(upnpPortMapping.getPort());
1032 argval[2].SetArgument("NewProtocol");
1033 argval[2].SetValue(upnpPortMapping.getProtocol());
1034 argval[3].SetArgument("NewInternalPort");
1035 argval[3].SetValue(upnpPortMapping.getPort());
1036 argval[4].SetArgument("NewInternalClient");
1037 argval[4].SetValue(ipAddress);
1038 argval[5].SetArgument("NewEnabled");
1039 argval[5].SetValue("1");
1040 argval[6].SetArgument("NewPortMappingDescription");
1041 argval[6].SetValue(upnpPortMapping.getDescription());
1042 argval[7].SetArgument("NewLeaseDuration");
1043 argval[7].SetValue("0");
1045 // Execute
1046 bool ret = true;
1047 for (ServiceMap::iterator it = m_ServiceMap.begin();
1048 it != m_ServiceMap.end(); ++it) {
1049 ret &= it->second->Execute(actionName, argval);
1052 return ret;
1056 bool CUPnPControlPoint::DeletePortMappings(
1057 std::vector<CUPnPPortMapping> &upnpPortMapping)
1059 std::ostringstream msg;
1060 if (!WanServiceDetected()) {
1061 msg << "UPnP Error: "
1062 "CUPnPControlPoint::DeletePortMapping: "
1063 "Wan Service not detected.";
1064 AddLogLineM(true, logUPnP, msg);
1065 return false;
1068 int n = upnpPortMapping.size();
1069 bool ok = false;
1071 // Check the number of port mappings before
1072 std::istringstream PortMappingNumberOfEntries(
1073 m_WanService->GetStateVariable(
1074 "PortMappingNumberOfEntries"));
1075 unsigned long oldNumberOfEntries;
1076 PortMappingNumberOfEntries >> oldNumberOfEntries;
1078 // Delete the enabled port mappings
1079 for (int i = 0; i < n; ++i) {
1080 if (upnpPortMapping[i].getEnabled() == "1") {
1081 // Delete the mapping from the control point
1082 // active mappings list
1083 PortMappingMap::iterator it =
1084 m_ActivePortMappingsMap.find(
1085 upnpPortMapping[i].getKey());
1086 if (it != m_ActivePortMappingsMap.end()) {
1087 m_ActivePortMappingsMap.erase(it);
1088 } else {
1089 msg << "UPnP Error: "
1090 "CUPnPControlPoint::DeletePortMapping: "
1091 "Mapping was not found in the active "
1092 "mapping map.";
1093 AddLogLineM(true, logUPnP, msg);
1096 // Delete the port mapping
1097 PrivateDeletePortMapping(upnpPortMapping[i]);
1101 // Debug only
1102 msg.str("");
1103 msg << "CUPnPControlPoint::DeletePortMappings: "
1104 "m_ActivePortMappingsMap.size() == " <<
1105 m_ActivePortMappingsMap.size();
1106 AddDebugLogLineM(false, logUPnP, msg);
1108 // Not very good, must find a better test
1109 PortMappingNumberOfEntries.str(
1110 m_WanService->GetStateVariable(
1111 "PortMappingNumberOfEntries"));
1112 unsigned long newNumberOfEntries;
1113 PortMappingNumberOfEntries >> newNumberOfEntries;
1114 ok = oldNumberOfEntries - newNumberOfEntries == 4;
1116 return ok;
1120 bool CUPnPControlPoint::PrivateDeletePortMapping(
1121 CUPnPPortMapping &upnpPortMapping)
1123 // Start building the action
1124 std::string actionName("DeletePortMapping");
1125 std::vector<CUPnPArgumentValue> argval(3);
1127 // Action parameters
1128 argval[0].SetArgument("NewRemoteHost");
1129 argval[0].SetValue("");
1130 argval[1].SetArgument("NewExternalPort");
1131 argval[1].SetValue(upnpPortMapping.getPort());
1132 argval[2].SetArgument("NewProtocol");
1133 argval[2].SetValue(upnpPortMapping.getProtocol());
1135 // Execute
1136 bool ret = true;
1137 for (ServiceMap::iterator it = m_ServiceMap.begin();
1138 it != m_ServiceMap.end(); ++it) {
1139 ret &= it->second->Execute(actionName, argval);
1142 return ret;
1146 // This function is static
1147 int CUPnPControlPoint::Callback(Upnp_EventType EventType, void *Event, void * /*Cookie*/)
1149 std::ostringstream msg;
1150 std::ostringstream msg2;
1151 // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
1152 // happen with a wrong cookie and... boom!
1153 // CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
1154 CUPnPControlPoint *upnpCP = CUPnPControlPoint::s_CtrlPoint;
1156 //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
1157 switch (EventType) {
1158 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
1159 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
1160 msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): ";
1161 msg2<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
1162 goto upnpDiscovery;
1163 case UPNP_DISCOVERY_SEARCH_RESULT: {
1164 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n");
1165 msg << "error(UPNP_DISCOVERY_SEARCH_RESULT): ";
1166 msg2<< "UPNP_DISCOVERY_SEARCH_RESULT: ";
1167 // UPnP Discovery
1168 upnpDiscovery:
1169 struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
1170 IXML_Document *doc = NULL;
1171 int ret;
1172 if (d_event->ErrCode != UPNP_E_SUCCESS) {
1173 msg << upnpCP->m_upnpLib.GetUPnPErrorMessage(d_event->ErrCode) << ".";
1174 AddDebugLogLineM(true, logUPnP, msg);
1176 // Get the XML tree device description in doc
1177 ret = UpnpDownloadXmlDoc(d_event->Location, &doc);
1178 if (ret != UPNP_E_SUCCESS) {
1179 msg << "Error retrieving device description from " <<
1180 d_event->Location << ": " <<
1181 upnpCP->m_upnpLib.GetUPnPErrorMessage(ret) << ".";
1182 AddDebugLogLineM(true, logUPnP, msg);
1183 } else {
1184 msg2 << "Retrieving device description from " <<
1185 d_event->Location << ".";
1186 AddDebugLogLineM(false, logUPnP, msg2);
1188 if (doc) {
1189 // Get the root node
1190 IXML_Element *root =
1191 upnpCP->m_upnpLib.Element_GetRootElement(doc);
1192 // Extract the URLBase
1193 const std::string urlBase = upnpCP->m_upnpLib.
1194 Element_GetChildValueByTag(root, "URLBase");
1195 // Get the root device
1196 IXML_Element *rootDevice = upnpCP->m_upnpLib.
1197 Element_GetFirstChildByTag(root, "device");
1198 // Extract the deviceType
1199 std::string devType(upnpCP->m_upnpLib.
1200 Element_GetChildValueByTag(rootDevice, "deviceType"));
1201 // Only add device if it is an InternetGatewayDevice
1202 if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) {
1203 // This condition can be used to auto-detect
1204 // the UPnP device we are interested in.
1205 // Obs.: Don't block the entry here on this
1206 // condition! There may be more than one device,
1207 // and the first that enters may not be the one
1208 // we are interested in!
1209 upnpCP->SetIGWDeviceDetected(true);
1210 // Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
1211 // we don't want to spam our logs.
1212 if (EventType != UPNP_DISCOVERY_ADVERTISEMENT_ALIVE) {
1213 msg.str("Internet Gateway Device Detected.");
1214 AddLogLineM(true, logUPnP, msg);
1216 // Add the root device to our list
1217 upnpCP->AddRootDevice(rootDevice, urlBase,
1218 d_event->Location, d_event->Expires);
1220 // Free the XML doc tree
1221 ixmlDocument_free(doc);
1223 break;
1225 case UPNP_DISCOVERY_SEARCH_TIMEOUT: {
1226 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1227 // Search timeout
1228 msg << "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1229 AddDebugLogLineM(false, logUPnP, msg);
1231 // Unlock the search timeout mutex
1232 upnpCP->m_WaitForSearchTimeoutMutex.Unlock();
1234 break;
1236 case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: {
1237 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
1238 // UPnP Device Removed
1239 struct Upnp_Discovery *dab_event = (struct Upnp_Discovery *)Event;
1240 if (dab_event->ErrCode != UPNP_E_SUCCESS) {
1241 msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
1242 upnpCP->m_upnpLib.GetUPnPErrorMessage(dab_event->ErrCode) <<
1243 ".";
1244 AddDebugLogLineM(true, logUPnP, msg);
1246 std::string devType = dab_event->DeviceType;
1247 // Check for an InternetGatewayDevice and removes it from the list
1248 std::transform(devType.begin(), devType.end(), devType.begin(), tolower);
1249 if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) {
1250 upnpCP->RemoveRootDevice(dab_event->DeviceId);
1252 break;
1254 case UPNP_EVENT_RECEIVED: {
1255 //fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1256 // Event reveived
1257 struct Upnp_Event *e_event = (struct Upnp_Event *)Event;
1258 const std::string Sid = e_event->Sid;
1259 // Parses the event
1260 upnpCP->OnEventReceived(Sid, e_event->EventKey, e_event->ChangedVariables);
1261 break;
1263 case UPNP_EVENT_SUBSCRIBE_COMPLETE:
1264 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
1265 msg << "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): ";
1266 goto upnpEventRenewalComplete;
1267 case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
1268 //fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
1269 msg << "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): ";
1270 goto upnpEventRenewalComplete;
1271 case UPNP_EVENT_RENEWAL_COMPLETE: {
1272 //fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n");
1273 msg << "error(UPNP_EVENT_RENEWAL_COMPLETE): ";
1274 upnpEventRenewalComplete:
1275 struct Upnp_Event_Subscribe *es_event =
1276 (struct Upnp_Event_Subscribe *)Event;
1277 if (es_event->ErrCode != UPNP_E_SUCCESS) {
1278 msg << "Error in Event Subscribe Callback";
1279 upnpCP->m_upnpLib.processUPnPErrorMessage(
1280 msg.str(), es_event->ErrCode, NULL, NULL);
1281 } else {
1282 #if 0
1283 TvCtrlPointHandleSubscribeUpdate(
1284 es_event->PublisherUrl,
1285 es_event->Sid,
1286 es_event->TimeOut );
1287 #endif
1290 break;
1293 case UPNP_EVENT_AUTORENEWAL_FAILED:
1294 //fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n");
1295 msg << "error(UPNP_EVENT_AUTORENEWAL_FAILED): ";
1296 msg2 << "UPNP_EVENT_AUTORENEWAL_FAILED: ";
1297 goto upnpEventSubscriptionExpired;
1298 case UPNP_EVENT_SUBSCRIPTION_EXPIRED: {
1299 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
1300 msg << "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): ";
1301 msg2 << "UPNP_EVENT_SUBSCRIPTION_EXPIRED: ";
1302 upnpEventSubscriptionExpired:
1303 struct Upnp_Event_Subscribe *es_event =
1304 (struct Upnp_Event_Subscribe *)Event;
1305 Upnp_SID newSID;
1306 int TimeOut = 1801;
1307 int ret = UpnpSubscribe(
1308 upnpCP->m_UPnPClientHandle,
1309 es_event->PublisherUrl,
1310 &TimeOut,
1311 newSID);
1312 if (ret != UPNP_E_SUCCESS) {
1313 msg << "Error Subscribing to EventURL";
1314 upnpCP->m_upnpLib.processUPnPErrorMessage(
1315 msg.str(), es_event->ErrCode, NULL, NULL);
1316 } else {
1317 ServiceMap::iterator it =
1318 upnpCP->m_ServiceMap.find(es_event->PublisherUrl);
1319 if (it != upnpCP->m_ServiceMap.end()) {
1320 CUPnPService &service = *(it->second);
1321 service.SetTimeout(TimeOut);
1322 service.SetSID(newSID);
1323 msg2 << "Re-subscribed to EventURL '" <<
1324 es_event->PublisherUrl <<
1325 "' with SID == '" <<
1326 newSID << "'.";
1327 AddDebugLogLineM(true, logUPnP, msg2);
1328 // In principle, we should test to see if the
1329 // service is the same. But here we only have one
1330 // service, so...
1331 upnpCP->RefreshPortMappings();
1332 } else {
1333 msg << "Error: did not find service " <<
1334 newSID << " in the service map.";
1335 AddDebugLogLineM(true, logUPnP, msg);
1338 break;
1340 case UPNP_CONTROL_ACTION_COMPLETE: {
1341 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n");
1342 // This is here if we choose to do this asynchronously
1343 struct Upnp_Action_Complete *a_event =
1344 (struct Upnp_Action_Complete *)Event;
1345 if (a_event->ErrCode != UPNP_E_SUCCESS) {
1346 upnpCP->m_upnpLib.processUPnPErrorMessage(
1347 "UpnpSendActionAsync",
1348 a_event->ErrCode, NULL,
1349 a_event->ActionResult);
1350 } else {
1351 // Check the response document
1352 upnpCP->m_upnpLib.ProcessActionResponse(
1353 a_event->ActionResult,
1354 "<UpnpSendActionAsync>");
1356 /* No need for any processing here, just print out results.
1357 * Service state table updates are handled by events.
1359 break;
1361 case UPNP_CONTROL_GET_VAR_COMPLETE: {
1362 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n");
1363 msg << "error(UPNP_CONTROL_GET_VAR_COMPLETE): ";
1364 struct Upnp_State_Var_Complete *sv_event =
1365 (struct Upnp_State_Var_Complete *)Event;
1366 if (sv_event->ErrCode != UPNP_E_SUCCESS) {
1367 msg << "m_UpnpGetServiceVarStatusAsync";
1368 upnpCP->m_upnpLib.processUPnPErrorMessage(
1369 msg.str(), sv_event->ErrCode, NULL, NULL);
1370 } else {
1371 #if 0
1372 // Warning: The use of UpnpGetServiceVarStatus and
1373 // UpnpGetServiceVarStatusAsync is deprecated by the
1374 // UPnP forum.
1375 TvCtrlPointHandleGetVar(
1376 sv_event->CtrlUrl,
1377 sv_event->StateVarName,
1378 sv_event->CurrentVal );
1379 #endif
1381 break;
1383 // ignore these cases, since this is not a device
1384 case UPNP_CONTROL_GET_VAR_REQUEST:
1385 //fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n");
1386 msg << "error(UPNP_CONTROL_GET_VAR_REQUEST): ";
1387 goto eventSubscriptionRequest;
1388 case UPNP_CONTROL_ACTION_REQUEST:
1389 //fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n");
1390 msg << "error(UPNP_CONTROL_ACTION_REQUEST): ";
1391 goto eventSubscriptionRequest;
1392 case UPNP_EVENT_SUBSCRIPTION_REQUEST:
1393 //fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
1394 msg << "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): ";
1395 eventSubscriptionRequest:
1396 msg << "This is not a UPnP Device, this is a UPnP Control Point, event ignored.";
1397 AddDebugLogLineM(true, logUPnP, msg);
1398 break;
1399 default:
1400 // Humm, this is not good, we forgot to handle something...
1401 fprintf(stderr,
1402 "Callback: default... Unknown event:'%d', not good.\n",
1403 EventType);
1404 msg << "error(UPnP::Callback): Event not handled:'" <<
1405 EventType << "'.";
1406 fprintf(stderr, "%s\n", msg.str().c_str());
1407 AddDebugLogLineM(true, logUPnP, msg);
1408 // Better not throw in the callback. Who would catch it?
1409 //throw CUPnPException(msg);
1410 break;
1413 return 0;
1417 void CUPnPControlPoint::OnEventReceived(
1418 const std::string &Sid,
1419 int EventKey,
1420 IXML_Document *ChangedVariablesDoc)
1422 std::ostringstream msg;
1423 msg << "UPNP_EVENT_RECEIVED:" <<
1424 "\n SID: " << Sid <<
1425 "\n Key: " << EventKey <<
1426 "\n Property list:";
1427 IXML_Element *root =
1428 m_upnpLib.Element_GetRootElement(ChangedVariablesDoc);
1429 IXML_Element *child =
1430 m_upnpLib.Element_GetFirstChild(root);
1431 if (child) {
1432 while (child) {
1433 IXML_Element *child2 =
1434 m_upnpLib.Element_GetFirstChild(child);
1435 const DOMString childTag =
1436 m_upnpLib.Element_GetTag(child2);
1437 std::string childValue =
1438 m_upnpLib.Element_GetTextValue(child2);
1439 msg << "\n " <<
1440 childTag << "='" <<
1441 childValue << "'";
1442 child = m_upnpLib.Element_GetNextSibling(child);
1444 } else {
1445 msg << "\n Empty property list.";
1447 AddDebugLogLineM(true, logUPnP, msg);
1448 // Freeing that doc segfaults. Probably should not be freed.
1449 //ixmlDocument_free(ChangedVariablesDoc);
1453 void CUPnPControlPoint::AddRootDevice(
1454 IXML_Element *rootDevice, const std::string &urlBase,
1455 const char *location, int expires)
1457 // Lock the Root Device List
1458 CUPnPMutexLocker lock(m_RootDeviceListMutex);
1460 // Root node's URLBase
1461 std::string OriginalURLBase(urlBase);
1462 std::string FixedURLBase(OriginalURLBase.empty() ?
1463 location :
1464 OriginalURLBase);
1466 // Get the UDN (Unique Device Name)
1467 std::string UDN(
1468 m_upnpLib.Element_GetChildValueByTag(rootDevice, "UDN"));
1469 RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
1470 bool alreadyAdded = it != m_RootDeviceMap.end();
1471 if (alreadyAdded) {
1472 // Just set the expires field
1473 it->second->SetExpires(expires);
1474 } else {
1475 // Add a new root device to the root device list
1476 CUPnPRootDevice *upnpRootDevice = new CUPnPRootDevice(
1477 *this, m_upnpLib, rootDevice,
1478 OriginalURLBase, FixedURLBase,
1479 location, expires);
1480 m_RootDeviceMap[upnpRootDevice->GetUDN()] = upnpRootDevice;
1485 void CUPnPControlPoint::RemoveRootDevice(const char *udn)
1487 // Lock the Root Device List
1488 CUPnPMutexLocker lock(m_RootDeviceListMutex);
1490 // Remove
1491 std::string UDN(udn);
1492 RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
1493 if (it != m_RootDeviceMap.end()) {
1494 delete it->second;
1495 m_RootDeviceMap.erase(UDN);
1500 void CUPnPControlPoint::Subscribe(CUPnPService &service)
1502 std::ostringstream msg;
1503 int errcode = UpnpSubscribe(m_UPnPClientHandle,
1504 service.GetAbsEventSubURL().c_str(),
1505 service.GetTimeoutAddr(),
1506 service.GetSID());
1507 if (errcode == UPNP_E_SUCCESS) {
1508 m_ServiceMap[service.GetAbsEventSubURL()] = &service;
1509 msg << "Successfully subscribed to service " <<
1510 service.GetServiceType() << ", absEventSubURL: " <<
1511 service.GetAbsEventSubURL() << ".";
1512 AddLogLineM(true, logUPnP, msg);
1514 IXML_Document *scpdDoc = NULL;
1515 errcode = UpnpDownloadXmlDoc(
1516 service.GetAbsSCPDURL().c_str(), &scpdDoc);
1517 if (errcode == UPNP_E_SUCCESS) {
1518 // Get the root node
1519 IXML_Element *scpdRoot =
1520 m_upnpLib.Element_GetRootElement(scpdDoc);
1521 CUPnPSCPD *scpd = new CUPnPSCPD(*this, m_upnpLib,
1522 scpdRoot, service.GetAbsSCPDURL());
1523 service.SetSCPD(scpd);
1524 } else {
1525 msg.str("");
1526 msg << "Error getting SCPD Document from " <<
1527 service.GetAbsSCPDURL() << ".";
1528 AddLogLineM(true, logUPnP, msg);
1530 } else {
1531 msg << "Error subscribing to service " <<
1532 service.GetServiceType() << ", absEventSubURL: " <<
1533 service.GetAbsEventSubURL() << ", error: " <<
1534 m_upnpLib.GetUPnPErrorMessage(errcode) << ".";
1535 goto error;
1538 return;
1540 // Error processing
1541 error:
1542 AddLogLineM(true, logUPnP, msg);
1546 void CUPnPControlPoint::Unsubscribe(CUPnPService &service)
1548 ServiceMap::iterator it = m_ServiceMap.find(service.GetAbsEventSubURL());
1549 m_ServiceMap.erase(it);
1550 UpnpUnSubscribe(m_UPnPClientHandle, service.GetSID());
1554 // File_checked_for_headers