Improve the code by static code analysis [3/3]: Style
[amule.git] / src / UPnPBase.cpp
blob45a276188ecc53b209465d8361c8a9ec940b0376
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2004-2011 Marcelo Roberto Jimenez ( phoenix@amule.org )
5 // Copyright (c) 2006-2011 aMule Team ( admin@amule.org / http://www.amule.org )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #ifdef HAVE_CONFIG_H
27 # include "config.h" // Needed for ENABLE_UPNP
28 #endif
30 #ifdef ENABLE_UPNP
32 #include "UPnPBase.h"
34 #include <algorithm> // For transform()
37 std::string stdEmptyString;
39 const char s_argument[] = "argument";
40 const char s_argumentList[] = "argumentList";
41 const char s_action[] = "action";
42 const char s_actionList[] = "actionList";
43 const char s_allowedValue[] = "allowedValue";
44 const char s_allowedValueList[] = "allowedValueList";
45 const char s_stateVariable[] = "stateVariable";
46 const char s_serviceStateTable[] = "serviceStateTable";
47 const char s_service[] = "service";
48 const char s_serviceList[] = "serviceList";
49 const char s_device[] = "device";
50 const char s_deviceList[] = "deviceList";
52 /**
53 * Case insensitive std::string comparison
55 bool stdStringIsEqualCI(const std::string &s1, const std::string &s2)
57 std::string ns1(s1);
58 std::string ns2(s2);
59 std::transform(ns1.begin(), ns1.end(), ns1.begin(), tolower);
60 std::transform(ns2.begin(), ns2.end(), ns2.begin(), tolower);
61 return ns1 == ns2;
65 CUPnPPortMapping::CUPnPPortMapping(
66 int port,
67 const std::string &protocol,
68 bool enabled,
69 const std::string &description)
71 m_port(),
72 m_protocol(protocol),
73 m_enabled(enabled ? "1" : "0"),
74 m_description(description),
75 m_key()
77 std::ostringstream oss;
78 oss << port;
79 m_port = oss.str();
80 m_key = m_protocol + m_port;
84 const std::string &CUPnPLib::UPNP_ROOT_DEVICE =
85 "upnp:rootdevice";
87 const std::string &CUPnPLib::UPNP_DEVICE_IGW =
88 "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
89 const std::string &CUPnPLib::UPNP_DEVICE_WAN =
90 "urn:schemas-upnp-org:device:WANDevice:1";
91 const std::string &CUPnPLib::UPNP_DEVICE_WAN_CONNECTION =
92 "urn:schemas-upnp-org:device:WANConnectionDevice:1";
93 const std::string &CUPnPLib::UPNP_DEVICE_LAN =
94 "urn:schemas-upnp-org:device:LANDevice:1";
96 const std::string &CUPnPLib::UPNP_SERVICE_LAYER3_FORWARDING =
97 "urn:schemas-upnp-org:service:Layer3Forwarding:1";
98 const std::string &CUPnPLib::UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG =
99 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1";
100 const std::string &CUPnPLib::UPNP_SERVICE_WAN_IP_CONNECTION =
101 "urn:schemas-upnp-org:service:WANIPConnection:1";
102 const std::string &CUPnPLib::UPNP_SERVICE_WAN_PPP_CONNECTION =
103 "urn:schemas-upnp-org:service:WANPPPConnection:1";
106 CUPnPLib::CUPnPLib(CUPnPControlPoint &ctrlPoint)
108 m_ctrlPoint(ctrlPoint)
113 std::string CUPnPLib::GetUPnPErrorMessage(int code) const
115 return UpnpGetErrorMessage(code);
119 std::string CUPnPLib::processUPnPErrorMessage(
120 const std::string &messsage,
121 int errorCode,
122 const DOMString errorString,
123 IXML_Document *doc) const
125 std::ostringstream msg;
126 if (errorString == NULL || *errorString == 0) {
127 errorString = "Not available";
129 if (errorCode > 0) {
130 msg << "Error: " <<
131 messsage <<
132 ": Error code :'";
133 if (doc) {
134 CUPnPError e(*this, doc);
135 msg << e.getErrorCode() <<
136 "', Error description :'" <<
137 e.getErrorDescription() <<
138 "'.";
139 } else {
140 msg << errorCode <<
141 "', Error description :'" <<
142 errorString <<
143 "'.";
145 AddLogLineU(false, logUPnP, msg);
146 } else {
147 msg << "Error: " <<
148 messsage <<
149 ": UPnP SDK error: " <<
150 GetUPnPErrorMessage(errorCode) <<
151 " (" << errorCode << ").";
152 AddLogLineU(false, logUPnP, msg);
155 return msg.str();
159 void CUPnPLib::ProcessActionResponse(
160 IXML_Document *RespDoc,
161 const std::string &actionName) const
163 std::ostringstream msg;
164 msg << "Response: ";
165 IXML_Element *root = Element_GetRootElement(RespDoc);
166 IXML_Element *child = Element_GetFirstChild(root);
167 if (child) {
168 while (child) {
169 const DOMString childTag = Element_GetTag(child);
170 std::string childValue = Element_GetTextValue(child);
171 msg << "\n " <<
172 childTag << "='" <<
173 childValue << "'";
174 child = Element_GetNextSibling(child);
176 } else {
177 msg << "\n Empty response for action '" <<
178 actionName << "'.";
180 AddDebugLogLineN(logUPnP, msg);
185 * \brief Returns the root node of a given document.
187 IXML_Element *CUPnPLib::Element_GetRootElement(
188 IXML_Document *doc) const
190 IXML_Element *root = reinterpret_cast<IXML_Element *>(
191 ixmlNode_getFirstChild(&doc->n));
193 return root;
198 * \brief Returns the first child of a given element.
200 IXML_Element *CUPnPLib::Element_GetFirstChild(
201 IXML_Element *parent) const
203 IXML_Node *child = ixmlNode_getFirstChild(&parent->n);
205 return reinterpret_cast<IXML_Element *>(child);
210 * \brief Returns the next sibling of a given child.
212 IXML_Element *CUPnPLib::Element_GetNextSibling(
213 IXML_Element *child) const
215 IXML_Node *sibling = ixmlNode_getNextSibling(&child->n);
217 return reinterpret_cast<IXML_Element *>(sibling);
222 * \brief Returns the element tag (name)
224 const DOMString CUPnPLib::Element_GetTag(
225 IXML_Element *element) const
227 const DOMString tag = ixmlNode_getNodeName(&element->n);
229 return tag;
234 * \brief Returns the TEXT node value of the current node.
236 const std::string CUPnPLib::Element_GetTextValue(
237 IXML_Element *element) const
239 if (!element) {
240 return stdEmptyString;
242 IXML_Node *text = ixmlNode_getFirstChild(&element->n);
243 const DOMString s = ixmlNode_getNodeValue(text);
244 std::string ret;
245 if (s) {
246 ret = s;
249 return ret;
254 * \brief Returns the TEXT node value of the first child matching tag.
256 const std::string CUPnPLib::Element_GetChildValueByTag(
257 IXML_Element *element,
258 const DOMString tag) const
260 IXML_Element *child =
261 Element_GetFirstChildByTag(element, tag);
263 return Element_GetTextValue(child);
268 * \brief Returns the first child element that matches the requested tag or
269 * NULL if not found.
271 IXML_Element *CUPnPLib::Element_GetFirstChildByTag(
272 IXML_Element *element,
273 const DOMString tag) const
275 if (!element || !tag) {
276 return NULL;
279 IXML_Node *child = ixmlNode_getFirstChild(&element->n);
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 = &element->n;
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(&element->n);
316 IXML_Node *attribute = ixmlNamedNodeMap_getNamedItem(NamedNodeMap, tag);
317 const DOMString s = ixmlNode_getNodeValue(attribute);
318 std::string ret;
319 if (s) {
320 ret = s;
322 ixmlNamedNodeMap_free(NamedNodeMap);
324 return ret;
328 CUPnPError::CUPnPError(
329 const CUPnPLib &upnpLib,
330 IXML_Document *errorDoc)
332 m_root (upnpLib.Element_GetRootElement(errorDoc)),
333 m_ErrorCode (upnpLib.Element_GetChildValueByTag(m_root, "errorCode")),
334 m_ErrorDescription(upnpLib.Element_GetChildValueByTag(m_root, "errorDescription"))
339 CUPnPArgument::CUPnPArgument(
340 const CUPnPControlPoint &upnpControlPoint,
341 CUPnPLib &upnpLib,
342 IXML_Element *argument,
343 const std::string &WXUNUSED(SCPDURL))
345 m_UPnPControlPoint(upnpControlPoint),
346 m_name (upnpLib.Element_GetChildValueByTag(argument, "name")),
347 m_direction (upnpLib.Element_GetChildValueByTag(argument, "direction")),
348 m_retval (upnpLib.Element_GetFirstChildByTag(argument, "retval")),
349 m_relatedStateVariable(upnpLib.Element_GetChildValueByTag(argument, "relatedStateVariable"))
351 std::ostringstream msg;
352 msg << "\n Argument:" <<
353 "\n name: " << m_name <<
354 "\n direction: " << m_direction <<
355 "\n retval: " << m_retval <<
356 "\n relatedStateVariable: " << m_relatedStateVariable;
357 AddDebugLogLineN(logUPnP, msg);
361 CUPnPAction::CUPnPAction(
362 const CUPnPControlPoint &upnpControlPoint,
363 CUPnPLib &upnpLib,
364 IXML_Element *action,
365 const std::string &SCPDURL)
367 m_UPnPControlPoint(upnpControlPoint),
368 m_ArgumentList(upnpControlPoint, upnpLib, action, SCPDURL),
369 m_name(upnpLib.Element_GetChildValueByTag(action, "name"))
371 std::ostringstream msg;
372 msg << "\n Action:" <<
373 "\n name: " << m_name;
374 AddDebugLogLineN(logUPnP, msg);
378 CUPnPAllowedValue::CUPnPAllowedValue(
379 const CUPnPControlPoint &upnpControlPoint,
380 CUPnPLib &upnpLib,
381 IXML_Element *allowedValue,
382 const std::string &WXUNUSED(SCPDURL))
384 m_UPnPControlPoint(upnpControlPoint),
385 m_allowedValue(upnpLib.Element_GetTextValue(allowedValue))
387 std::ostringstream msg;
388 msg << "\n AllowedValue:" <<
389 "\n allowedValue: " << m_allowedValue;
390 AddDebugLogLineN(logUPnP, msg);
394 CUPnPStateVariable::CUPnPStateVariable(
395 const CUPnPControlPoint &upnpControlPoint,
396 CUPnPLib &upnpLib,
397 IXML_Element *stateVariable,
398 const std::string &SCPDURL)
400 m_UPnPControlPoint(upnpControlPoint),
401 m_AllowedValueList(upnpControlPoint, upnpLib, stateVariable, SCPDURL),
402 m_name (upnpLib.Element_GetChildValueByTag(stateVariable, "name")),
403 m_dataType (upnpLib.Element_GetChildValueByTag(stateVariable, "dataType")),
404 m_defaultValue(upnpLib.Element_GetChildValueByTag(stateVariable, "defaultValue")),
405 m_sendEvents (upnpLib.Element_GetAttributeByTag (stateVariable, "sendEvents"))
407 std::ostringstream msg;
408 msg << "\n StateVariable:" <<
409 "\n name: " << m_name <<
410 "\n dataType: " << m_dataType <<
411 "\n defaultValue: " << m_defaultValue <<
412 "\n sendEvents: " << m_sendEvents;
413 AddDebugLogLineN(logUPnP, msg);
417 CUPnPSCPD::CUPnPSCPD(
418 const CUPnPControlPoint &upnpControlPoint,
419 CUPnPLib &upnpLib,
420 IXML_Element *scpd,
421 const std::string &SCPDURL)
423 m_UPnPControlPoint(upnpControlPoint),
424 m_ActionList(upnpControlPoint, upnpLib, scpd, SCPDURL),
425 m_ServiceStateTable(upnpControlPoint, upnpLib, scpd, SCPDURL),
426 m_SCPDURL(SCPDURL)
431 CUPnPArgumentValue::CUPnPArgumentValue()
433 m_argument(),
434 m_value()
439 CUPnPArgumentValue::CUPnPArgumentValue(
440 const std::string &argument, const std::string &value)
442 m_argument(argument),
443 m_value(value)
448 CUPnPService::CUPnPService(
449 const CUPnPControlPoint &upnpControlPoint,
450 CUPnPLib &upnpLib,
451 IXML_Element *service,
452 const std::string &URLBase)
454 m_UPnPControlPoint(upnpControlPoint),
455 m_upnpLib(upnpLib),
456 m_serviceType(upnpLib.Element_GetChildValueByTag(service, "serviceType")),
457 m_serviceId (upnpLib.Element_GetChildValueByTag(service, "serviceId")),
458 m_SCPDURL (upnpLib.Element_GetChildValueByTag(service, "SCPDURL")),
459 m_controlURL (upnpLib.Element_GetChildValueByTag(service, "controlURL")),
460 m_eventSubURL(upnpLib.Element_GetChildValueByTag(service, "eventSubURL")),
461 m_timeout(1801),
462 m_SCPD(NULL)
464 std::ostringstream msg;
465 int errcode;
467 std::vector<char> vscpdURL(URLBase.length() + m_SCPDURL.length() + 1);
468 char *scpdURL = &vscpdURL[0];
469 errcode = UpnpResolveURL(
470 URLBase.c_str(),
471 m_SCPDURL.c_str(),
472 scpdURL);
473 if( errcode != UPNP_E_SUCCESS ) {
474 msg << "Error generating scpdURL from " <<
475 "|" << URLBase << "|" <<
476 m_SCPDURL << "|.";
477 AddDebugLogLineN(logUPnP, msg);
478 } else {
479 m_absSCPDURL = scpdURL;
482 std::vector<char> vcontrolURL(
483 URLBase.length() + m_controlURL.length() + 1);
484 char *controlURL = &vcontrolURL[0];
485 errcode = UpnpResolveURL(
486 URLBase.c_str(),
487 m_controlURL.c_str(),
488 controlURL);
489 if( errcode != UPNP_E_SUCCESS ) {
490 msg << "Error generating controlURL from " <<
491 "|" << URLBase << "|" <<
492 m_controlURL << "|.";
493 AddDebugLogLineN(logUPnP, msg);
494 } else {
495 m_absControlURL = controlURL;
498 std::vector<char> veventURL(
499 URLBase.length() + m_eventSubURL.length() + 1);
500 char *eventURL = &veventURL[0];
501 errcode = UpnpResolveURL(
502 URLBase.c_str(),
503 m_eventSubURL.c_str(),
504 eventURL);
505 if( errcode != UPNP_E_SUCCESS ) {
506 msg << "Error generating eventURL from " <<
507 "|" << URLBase << "|" <<
508 m_eventSubURL << "|.";
509 AddDebugLogLineN(logUPnP, msg);
510 } else {
511 m_absEventSubURL = eventURL;
514 msg << "\n Service:" <<
515 "\n serviceType: " << m_serviceType <<
516 "\n serviceId: " << m_serviceId <<
517 "\n SCPDURL: " << m_SCPDURL <<
518 "\n absSCPDURL: " << m_absSCPDURL <<
519 "\n controlURL: " << m_controlURL <<
520 "\n absControlURL: " << m_absControlURL <<
521 "\n eventSubURL: " << m_eventSubURL <<
522 "\n absEventSubURL: " << m_absEventSubURL;
523 AddDebugLogLineN(logUPnP, msg);
525 if (m_serviceType == upnpLib.UPNP_SERVICE_WAN_IP_CONNECTION ||
526 m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION) {
527 #if 0
528 m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION ||
529 m_serviceType == upnpLib.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG ||
530 m_serviceType == upnpLib.UPNP_SERVICE_LAYER3_FORWARDING) {
531 #endif
532 #if 0
533 //#warning Delete this code on release.
534 if (!upnpLib.m_ctrlPoint.WanServiceDetected()) {
535 // This condition can be used to suspend the parse
536 // of the XML tree.
537 #endif
538 //#warning Delete this code when m_WanService is no longer used.
539 upnpLib.m_ctrlPoint.SetWanService(this);
540 // Log it
541 msg.str("");
542 msg << "WAN Service Detected: '" <<
543 m_serviceType << "'.";
544 AddDebugLogLineC(logUPnP, msg);
545 // Subscribe
546 upnpLib.m_ctrlPoint.Subscribe(*this);
547 #if 0
548 //#warning Delete this code on release.
549 } else {
550 msg.str("");
551 msg << "WAN service detected again: '" <<
552 m_serviceType <<
553 "'. Will only use the first instance.";
554 AddDebugLogLineC(logUPnP, msg);
556 #endif
557 } else {
558 msg.str("");
559 msg << "Uninteresting service detected: '" <<
560 m_serviceType << "'. Ignoring.";
561 AddDebugLogLineC(logUPnP, msg);
566 CUPnPService::~CUPnPService()
571 bool CUPnPService::Execute(
572 const std::string &ActionName,
573 const std::vector<CUPnPArgumentValue> &ArgValue) const
575 std::ostringstream msg;
576 if (m_SCPD.get() == NULL) {
577 msg << "Service without SCPD Document, cannot execute action '" << ActionName <<
578 "' for service '" << GetServiceType() << "'.";
579 AddDebugLogLineN(logUPnP, msg);
580 return false;
582 std::ostringstream msgAction("Sending action ");
583 // Check for correct action name
584 ActionList::const_iterator itAction =
585 m_SCPD->GetActionList().find(ActionName);
586 if (itAction == m_SCPD->GetActionList().end()) {
587 msg << "Invalid action name '" << ActionName <<
588 "' for service '" << GetServiceType() << "'.";
589 AddDebugLogLineN(logUPnP, msg);
590 return false;
592 msgAction << ActionName << "(";
593 bool firstTime = true;
594 // Check for correct Argument/Value pairs
595 const CUPnPAction &action = *(itAction->second);
596 for (unsigned int i = 0; i < ArgValue.size(); ++i) {
597 ArgumentList::const_iterator itArg =
598 action.GetArgumentList().find(ArgValue[i].GetArgument());
599 if (itArg == action.GetArgumentList().end()) {
600 msg << "Invalid argument name '" << ArgValue[i].GetArgument() <<
601 "' for action '" << action.GetName() <<
602 "' for service '" << GetServiceType() << "'.";
603 AddDebugLogLineN(logUPnP, msg);
604 return false;
606 const CUPnPArgument &argument = *(itArg->second);
607 if (tolower(argument.GetDirection()[0]) != 'i' ||
608 tolower(argument.GetDirection()[1]) != 'n') {
609 msg << "Invalid direction for argument '" <<
610 ArgValue[i].GetArgument() <<
611 "' for action '" << action.GetName() <<
612 "' for service '" << GetServiceType() << "'.";
613 AddDebugLogLineN(logUPnP, msg);
614 return false;
616 const std::string relatedStateVariableName =
617 argument.GetRelatedStateVariable();
618 if (!relatedStateVariableName.empty()) {
619 ServiceStateTable::const_iterator itSVT =
620 m_SCPD->GetServiceStateTable().
621 find(relatedStateVariableName);
622 if (itSVT == m_SCPD->GetServiceStateTable().end()) {
623 msg << "Inconsistent Service State Table, did not find '" <<
624 relatedStateVariableName <<
625 "' for argument '" << argument.GetName() <<
626 "' for action '" << action.GetName() <<
627 "' for service '" << GetServiceType() << "'.";
628 AddDebugLogLineN(logUPnP, msg);
629 return false;
631 const CUPnPStateVariable &stateVariable = *(itSVT->second);
632 if ( !stateVariable.GetAllowedValueList().empty() &&
633 stateVariable.GetAllowedValueList().find(ArgValue[i].GetValue()) ==
634 stateVariable.GetAllowedValueList().end()) {
635 msg << "Value not allowed '" << ArgValue[i].GetValue() <<
636 "' for state variable '" << relatedStateVariableName <<
637 "' for argument '" << argument.GetName() <<
638 "' for action '" << action.GetName() <<
639 "' for service '" << GetServiceType() << "'.";
640 AddDebugLogLineN(logUPnP, msg);
641 return false;
644 if (firstTime) {
645 firstTime = false;
646 } else {
647 msgAction << ", ";
649 msgAction <<
650 ArgValue[i].GetArgument() <<
651 "='" <<
652 ArgValue[i].GetValue() <<
653 "'";
655 msgAction << ")";
656 AddDebugLogLineN(logUPnP, msgAction);
657 // Everything is ok, make the action
658 IXML_Document *ActionDoc = NULL;
659 if (!ArgValue.empty()) {
660 for (unsigned int i = 0; i < ArgValue.size(); ++i) {
661 int ret = UpnpAddToAction(
662 &ActionDoc,
663 action.GetName().c_str(),
664 GetServiceType().c_str(),
665 ArgValue[i].GetArgument().c_str(),
666 ArgValue[i].GetValue().c_str());
667 if (ret != UPNP_E_SUCCESS) {
668 m_upnpLib.processUPnPErrorMessage(
669 "UpnpAddToAction", ret, NULL, NULL);
670 return false;
673 } else {
674 ActionDoc = UpnpMakeAction(
675 action.GetName().c_str(),
676 GetServiceType().c_str(),
677 0, NULL);
678 if (!ActionDoc) {
679 msg << "Error: UpnpMakeAction returned NULL.";
680 AddLogLineU(false, logUPnP, msg);
681 return false;
684 #if 0
685 // Send the action asynchronously
686 UpnpSendActionAsync(
687 m_UPnPControlPoint.GetUPnPClientHandle(),
688 GetAbsControlURL().c_str(),
689 GetServiceType().c_str(),
690 NULL, ActionDoc,
691 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
692 NULL);
693 return true;
694 #endif
696 // Send the action synchronously
697 IXML_Document *RespDoc = NULL;
698 int ret = UpnpSendAction(
699 m_UPnPControlPoint.GetUPnPClientHandle(),
700 GetAbsControlURL().c_str(),
701 GetServiceType().c_str(),
702 NULL, ActionDoc, &RespDoc);
703 if (ret != UPNP_E_SUCCESS) {
704 m_upnpLib.processUPnPErrorMessage(
705 "UpnpSendAction", ret, NULL, RespDoc);
706 ixmlDocument_free(ActionDoc);
707 ixmlDocument_free(RespDoc);
708 return false;
710 ixmlDocument_free(ActionDoc);
712 // Check the response document
713 m_upnpLib.ProcessActionResponse(
714 RespDoc, action.GetName());
716 // Free the response document
717 ixmlDocument_free(RespDoc);
719 return true;
723 const std::string CUPnPService::GetStateVariable(
724 const std::string &stateVariableName) const
726 std::ostringstream msg;
727 DOMString StVarVal;
728 int ret = UpnpGetServiceVarStatus(
729 m_UPnPControlPoint.GetUPnPClientHandle(),
730 GetAbsControlURL().c_str(),
731 stateVariableName.c_str(),
732 &StVarVal);
733 if (ret != UPNP_E_SUCCESS) {
734 msg << "GetStateVariable(\"" <<
735 stateVariableName <<
736 "\"): in a call to UpnpGetServiceVarStatus";
737 m_upnpLib.processUPnPErrorMessage(
738 msg.str(), ret, StVarVal, NULL);
739 return stdEmptyString;
741 msg << "GetStateVariable: " <<
742 stateVariableName <<
743 "='" <<
744 StVarVal <<
745 "'.";
746 AddDebugLogLineN(logUPnP, msg);
747 return stdEmptyString;
751 CUPnPDevice::CUPnPDevice(
752 const CUPnPControlPoint &upnpControlPoint,
753 CUPnPLib &upnpLib,
754 IXML_Element *device,
755 const std::string &URLBase)
757 m_UPnPControlPoint(upnpControlPoint),
758 m_DeviceList(upnpControlPoint, upnpLib, device, URLBase),
759 m_ServiceList(upnpControlPoint, upnpLib, device, URLBase),
760 m_deviceType (upnpLib.Element_GetChildValueByTag(device, "deviceType")),
761 m_friendlyName (upnpLib.Element_GetChildValueByTag(device, "friendlyName")),
762 m_manufacturer (upnpLib.Element_GetChildValueByTag(device, "manufacturer")),
763 m_manufacturerURL (upnpLib.Element_GetChildValueByTag(device, "manufacturerURL")),
764 m_modelDescription (upnpLib.Element_GetChildValueByTag(device, "modelDescription")),
765 m_modelName (upnpLib.Element_GetChildValueByTag(device, "modelName")),
766 m_modelNumber (upnpLib.Element_GetChildValueByTag(device, "modelNumber")),
767 m_modelURL (upnpLib.Element_GetChildValueByTag(device, "modelURL")),
768 m_serialNumber (upnpLib.Element_GetChildValueByTag(device, "serialNumber")),
769 m_UDN (upnpLib.Element_GetChildValueByTag(device, "UDN")),
770 m_UPC (upnpLib.Element_GetChildValueByTag(device, "UPC")),
771 m_presentationURL (upnpLib.Element_GetChildValueByTag(device, "presentationURL"))
773 std::ostringstream msg;
774 int presURLlen = strlen(URLBase.c_str()) +
775 strlen(m_presentationURL.c_str()) + 2;
776 std::vector<char> vpresURL(presURLlen);
777 char* presURL = &vpresURL[0];
778 int errcode = UpnpResolveURL(
779 URLBase.c_str(),
780 m_presentationURL.c_str(),
781 presURL);
782 if (errcode != UPNP_E_SUCCESS) {
783 msg << "Error generating presentationURL from " <<
784 "|" << URLBase << "|" <<
785 m_presentationURL << "|.";
786 AddDebugLogLineN(logUPnP, msg);
787 } else {
788 m_presentationURL = presURL;
791 msg.str("");
792 msg << "\n Device: " <<
793 "\n friendlyName: " << m_friendlyName <<
794 "\n deviceType: " << m_deviceType <<
795 "\n manufacturer: " << m_manufacturer <<
796 "\n manufacturerURL: " << m_manufacturerURL <<
797 "\n modelDescription: " << m_modelDescription <<
798 "\n modelName: " << m_modelName <<
799 "\n modelNumber: " << m_modelNumber <<
800 "\n modelURL: " << m_modelURL <<
801 "\n serialNumber: " << m_serialNumber <<
802 "\n UDN: " << m_UDN <<
803 "\n UPC: " << m_UPC <<
804 "\n presentationURL: " << m_presentationURL;
805 AddDebugLogLineN(logUPnP, msg);
809 CUPnPRootDevice::CUPnPRootDevice(
810 const CUPnPControlPoint &upnpControlPoint,
811 CUPnPLib &upnpLib,
812 IXML_Element *rootDevice,
813 const std::string &OriginalURLBase,
814 const std::string &FixedURLBase,
815 const char *location,
816 int expires)
818 CUPnPDevice(upnpControlPoint, upnpLib, rootDevice, FixedURLBase),
819 m_UPnPControlPoint(upnpControlPoint),
820 m_URLBase(OriginalURLBase),
821 m_location(location),
822 m_expires(expires)
824 std::ostringstream msg;
825 msg <<
826 "\n Root Device: " <<
827 "\n URLBase: " << m_URLBase <<
828 "\n Fixed URLBase: " << FixedURLBase <<
829 "\n location: " << m_location <<
830 "\n expires: " << m_expires;
831 AddDebugLogLineN(logUPnP, msg);
835 CUPnPControlPoint *CUPnPControlPoint::s_CtrlPoint = NULL;
838 CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort)
840 m_upnpLib(*this),
841 m_UPnPClientHandle(),
842 m_RootDeviceMap(),
843 m_ServiceMap(),
844 m_ActivePortMappingsMap(),
845 m_RootDeviceListMutex(),
846 m_IGWDeviceDetected(false),
847 m_WanService(NULL)
849 // Pointer to self
850 s_CtrlPoint = this;
851 // Null string at first
852 std::ostringstream msg;
854 // Start UPnP
855 int ret;
856 char *ipAddress = NULL;
857 unsigned short port = 0;
858 ret = UpnpInit(ipAddress, udpPort);
859 if (ret != UPNP_E_SUCCESS) {
860 msg << "error(UpnpInit): Error code ";
861 goto error;
863 port = UpnpGetServerPort();
864 ipAddress = UpnpGetServerIpAddress();
865 msg << "bound to " << ipAddress << ":" <<
866 port << ".";
867 AddLogLineU(false, logUPnP, msg);
868 msg.str("");
869 ret = UpnpRegisterClient(
870 static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
871 &m_UPnPClientHandle,
872 &m_UPnPClientHandle);
873 if (ret != UPNP_E_SUCCESS) {
874 msg << "error(UpnpRegisterClient): Error registering callback: ";
875 goto error;
878 // We could ask for just the right device here. If the root device
879 // contains the device we want, it will respond with the full XML doc,
880 // including the root device and every sub-device it has.
882 // But lets find out what we have in our network by calling UPNP_ROOT_DEVICE.
884 // We should not search twice, because this will produce two
885 // UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
886 // on the mutex.
887 ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_ROOT_DEVICE.c_str(), NULL);
888 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this);
889 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this);
890 //ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this);
891 if (ret != UPNP_E_SUCCESS) {
892 msg << "error(UpnpSearchAsync): Error sending search request: ";
893 goto error;
896 // Wait for the UPnP initialization to complete.
898 // Lock the search timeout mutex
899 m_WaitForSearchTimeoutMutex.Lock();
901 // Lock it again, so that we block. Unlocking will only happen
902 // when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
903 // callback.
904 CUPnPMutexLocker lock(m_WaitForSearchTimeoutMutex);
906 return;
908 // Error processing
909 error:
910 UpnpFinish();
911 msg << ret << ": " << m_upnpLib.GetUPnPErrorMessage(ret) << ".";
912 throw CUPnPException(msg);
916 CUPnPControlPoint::~CUPnPControlPoint()
918 for( RootDeviceMap::iterator it = m_RootDeviceMap.begin();
919 it != m_RootDeviceMap.end();
920 ++it) {
921 delete it->second;
923 // Remove all first
924 // RemoveAll();
925 UpnpUnRegisterClient(m_UPnPClientHandle);
926 UpnpFinish();
930 bool CUPnPControlPoint::AddPortMappings(
931 std::vector<CUPnPPortMapping> &upnpPortMapping)
933 std::ostringstream msg;
934 if (!WanServiceDetected()) {
935 msg << "UPnP Error: "
936 "CUPnPControlPoint::AddPortMapping: "
937 "WAN Service not detected.";
938 AddLogLineU(true, logUPnP, msg);
939 return false;
942 int n = upnpPortMapping.size();
943 bool ok = false;
945 // Check the number of port mappings before
946 std::istringstream PortMappingNumberOfEntries(
947 m_WanService->GetStateVariable(
948 "PortMappingNumberOfEntries"));
949 unsigned long oldNumberOfEntries;
950 PortMappingNumberOfEntries >> oldNumberOfEntries;
952 // Add the enabled port mappings
953 for (int i = 0; i < n; ++i) {
954 if (upnpPortMapping[i].getEnabled() == "1") {
955 // Add the mapping to the control point
956 // active mappings list
957 m_ActivePortMappingsMap[upnpPortMapping[i].getKey()] =
958 upnpPortMapping[i];
960 // Add the port mapping
961 PrivateAddPortMapping(upnpPortMapping[i]);
965 // Test some variables, this is deprecated, might not work
966 // with some routers
967 m_WanService->GetStateVariable("ConnectionType");
968 m_WanService->GetStateVariable("PossibleConnectionTypes");
969 m_WanService->GetStateVariable("ConnectionStatus");
970 m_WanService->GetStateVariable("Uptime");
971 m_WanService->GetStateVariable("LastConnectionError");
972 m_WanService->GetStateVariable("RSIPAvailable");
973 m_WanService->GetStateVariable("NATEnabled");
974 m_WanService->GetStateVariable("ExternalIPAddress");
975 m_WanService->GetStateVariable("PortMappingNumberOfEntries");
976 m_WanService->GetStateVariable("PortMappingLeaseDuration");
978 // Just for testing
979 std::vector<CUPnPArgumentValue> argval;
980 argval.resize(0);
981 m_WanService->Execute("GetStatusInfo", argval);
983 #if 0
984 // These do not work. Their value must be requested for a
985 // specific port mapping.
986 m_WanService->GetStateVariable("PortMappingEnabled");
987 m_WanService->GetStateVariable("RemoteHost");
988 m_WanService->GetStateVariable("ExternalPort");
989 m_WanService->GetStateVariable("InternalPort");
990 m_WanService->GetStateVariable("PortMappingProtocol");
991 m_WanService->GetStateVariable("InternalClient");
992 m_WanService->GetStateVariable("PortMappingDescription");
993 #endif
995 // Debug only
996 msg.str("");
997 msg << "CUPnPControlPoint::DeletePortMappings: "
998 "m_ActivePortMappingsMap.size() == " <<
999 m_ActivePortMappingsMap.size();
1000 AddDebugLogLineN(logUPnP, msg);
1002 // Not very good, must find a better test
1003 PortMappingNumberOfEntries.str(
1004 m_WanService->GetStateVariable(
1005 "PortMappingNumberOfEntries"));
1006 unsigned long newNumberOfEntries;
1007 PortMappingNumberOfEntries >> newNumberOfEntries;
1008 ok = newNumberOfEntries - oldNumberOfEntries == 4;
1010 return ok;
1014 void CUPnPControlPoint::RefreshPortMappings()
1016 for ( PortMappingMap::iterator it = m_ActivePortMappingsMap.begin();
1017 it != m_ActivePortMappingsMap.end();
1018 ++it) {
1019 PrivateAddPortMapping(it->second);
1022 // For testing
1023 m_WanService->GetStateVariable("PortMappingNumberOfEntries");
1027 bool CUPnPControlPoint::PrivateAddPortMapping(
1028 CUPnPPortMapping &upnpPortMapping)
1030 // Get an IP address. The UPnP server one must do.
1031 std::string ipAddress(UpnpGetServerIpAddress());
1033 // Start building the action
1034 std::string actionName("AddPortMapping");
1035 std::vector<CUPnPArgumentValue> argval(8);
1037 // Action parameters
1038 argval[0].SetArgument("NewRemoteHost");
1039 argval[0].SetValue("");
1040 argval[1].SetArgument("NewExternalPort");
1041 argval[1].SetValue(upnpPortMapping.getPort());
1042 argval[2].SetArgument("NewProtocol");
1043 argval[2].SetValue(upnpPortMapping.getProtocol());
1044 argval[3].SetArgument("NewInternalPort");
1045 argval[3].SetValue(upnpPortMapping.getPort());
1046 argval[4].SetArgument("NewInternalClient");
1047 argval[4].SetValue(ipAddress);
1048 argval[5].SetArgument("NewEnabled");
1049 argval[5].SetValue("1");
1050 argval[6].SetArgument("NewPortMappingDescription");
1051 argval[6].SetValue(upnpPortMapping.getDescription());
1052 argval[7].SetArgument("NewLeaseDuration");
1053 argval[7].SetValue("0");
1055 // Execute
1056 bool ret = true;
1057 for (ServiceMap::iterator it = m_ServiceMap.begin();
1058 it != m_ServiceMap.end(); ++it) {
1059 ret &= it->second->Execute(actionName, argval);
1062 return ret;
1066 bool CUPnPControlPoint::DeletePortMappings(
1067 std::vector<CUPnPPortMapping> &upnpPortMapping)
1069 std::ostringstream msg;
1070 if (!WanServiceDetected()) {
1071 msg << "UPnP Error: "
1072 "CUPnPControlPoint::DeletePortMapping: "
1073 "WAN Service not detected.";
1074 AddLogLineU(true, logUPnP, msg);
1075 return false;
1078 int n = upnpPortMapping.size();
1079 bool ok = false;
1081 // Check the number of port mappings before
1082 std::istringstream PortMappingNumberOfEntries(
1083 m_WanService->GetStateVariable(
1084 "PortMappingNumberOfEntries"));
1085 unsigned long oldNumberOfEntries;
1086 PortMappingNumberOfEntries >> oldNumberOfEntries;
1088 // Delete the enabled port mappings
1089 for (int i = 0; i < n; ++i) {
1090 if (upnpPortMapping[i].getEnabled() == "1") {
1091 // Delete the mapping from the control point
1092 // active mappings list
1093 PortMappingMap::iterator it =
1094 m_ActivePortMappingsMap.find(
1095 upnpPortMapping[i].getKey());
1096 if (it != m_ActivePortMappingsMap.end()) {
1097 m_ActivePortMappingsMap.erase(it);
1098 } else {
1099 msg << "UPnP Error: "
1100 "CUPnPControlPoint::DeletePortMapping: "
1101 "Mapping was not found in the active "
1102 "mapping map.";
1103 AddLogLineU(true, logUPnP, msg);
1106 // Delete the port mapping
1107 PrivateDeletePortMapping(upnpPortMapping[i]);
1111 // Debug only
1112 msg.str("");
1113 msg << "CUPnPControlPoint::DeletePortMappings: "
1114 "m_ActivePortMappingsMap.size() == " <<
1115 m_ActivePortMappingsMap.size();
1116 AddDebugLogLineN(logUPnP, msg);
1118 // Not very good, must find a better test
1119 PortMappingNumberOfEntries.str(
1120 m_WanService->GetStateVariable(
1121 "PortMappingNumberOfEntries"));
1122 unsigned long newNumberOfEntries;
1123 PortMappingNumberOfEntries >> newNumberOfEntries;
1124 ok = oldNumberOfEntries - newNumberOfEntries == 4;
1126 return ok;
1130 bool CUPnPControlPoint::PrivateDeletePortMapping(
1131 CUPnPPortMapping &upnpPortMapping)
1133 // Start building the action
1134 std::string actionName("DeletePortMapping");
1135 std::vector<CUPnPArgumentValue> argval(3);
1137 // Action parameters
1138 argval[0].SetArgument("NewRemoteHost");
1139 argval[0].SetValue("");
1140 argval[1].SetArgument("NewExternalPort");
1141 argval[1].SetValue(upnpPortMapping.getPort());
1142 argval[2].SetArgument("NewProtocol");
1143 argval[2].SetValue(upnpPortMapping.getProtocol());
1145 // Execute
1146 bool ret = true;
1147 for (ServiceMap::iterator it = m_ServiceMap.begin();
1148 it != m_ServiceMap.end(); ++it) {
1149 ret &= it->second->Execute(actionName, argval);
1152 return ret;
1156 // This function is static
1157 int CUPnPControlPoint::Callback(Upnp_EventType EventType, void *Event, void * /*Cookie*/)
1159 std::ostringstream msg;
1160 std::ostringstream msg2;
1161 // Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
1162 // happen with a wrong cookie and... boom!
1163 // CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
1164 CUPnPControlPoint *upnpCP = CUPnPControlPoint::s_CtrlPoint;
1166 //fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
1167 switch (EventType) {
1168 case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
1169 //fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
1170 msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): ";
1171 msg2<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
1172 goto upnpDiscovery;
1173 case UPNP_DISCOVERY_SEARCH_RESULT: {
1174 //fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n");
1175 msg << "error(UPNP_DISCOVERY_SEARCH_RESULT): ";
1176 msg2<< "UPNP_DISCOVERY_SEARCH_RESULT: ";
1177 // UPnP Discovery
1178 upnpDiscovery:
1179 struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
1180 IXML_Document *doc = NULL;
1181 int ret;
1182 if (d_event->ErrCode != UPNP_E_SUCCESS) {
1183 msg << upnpCP->m_upnpLib.GetUPnPErrorMessage(d_event->ErrCode) << ".";
1184 AddDebugLogLineC(logUPnP, msg);
1186 // Get the XML tree device description in doc
1187 ret = UpnpDownloadXmlDoc(d_event->Location, &doc);
1188 if (ret != UPNP_E_SUCCESS) {
1189 msg << "Error retrieving device description from " <<
1190 d_event->Location << ": " <<
1191 upnpCP->m_upnpLib.GetUPnPErrorMessage(ret) <<
1192 "(" << ret << ").";
1193 AddDebugLogLineC(logUPnP, msg);
1194 } else {
1195 msg2 << "Retrieving device description from " <<
1196 d_event->Location << ".";
1197 AddDebugLogLineN(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 AddDebugLogLineN(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 AddDebugLogLineC(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 AddDebugLogLineC(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 AddDebugLogLineC(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 AddDebugLogLineC(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 AddDebugLogLineC(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 AddDebugLogLineC(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());
1574 #endif /* ENABLE_UPNP */