aboutsummaryrefslogtreecommitdiffstats
path: root/src/modules/upnp/service.cpp
diff options
context:
space:
mode:
authorGravatar Fabio Bas2008-08-09 11:32:17 +0000
committerGravatar Fabio Bas2008-08-09 11:32:17 +0000
commitef736944c9d8a7833bb1b6e7a78b8266554d5cea (patch)
tree3946bb8b8f3e4ca21aeda2147bc72baae85a996f /src/modules/upnp/service.cpp
parentupdated german translation (diff)
downloadKVIrc-ef736944c9d8a7833bb1b6e7a78b8266554d5cea.tar.gz
KVIrc-ef736944c9d8a7833bb1b6e7a78b8266554d5cea.tar.bz2
KVIrc-ef736944c9d8a7833bb1b6e7a78b8266554d5cea.zip
added first test of a upnp module (gateway control point that enables port-forwarding and getting external ip address); based on code from Diederik van der Boor
git-svn-id: https://svn.kvirc.de/svn/trunk/kvirc@2219 17fca916-40b9-46aa-a4ea-0a15b648b75c
Diffstat (limited to 'src/modules/upnp/service.cpp')
-rw-r--r--src/modules/upnp/service.cpp286
1 files changed, 286 insertions, 0 deletions
diff --git a/src/modules/upnp/service.cpp b/src/modules/upnp/service.cpp
new file mode 100644
index 000000000..055b968de
--- /dev/null
+++ b/src/modules/upnp/service.cpp
@@ -0,0 +1,286 @@
+//=============================================================================
+//
+// Creation date : Fri Aug 08 18:00:00 2000 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2008 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// Original Copyright following:
+//=============================================================================
+
+/***************************************************************************
+ service.cpp - description
+ -------------------
+ begin : Sun Jul 24 2005
+ copyright : (C) 2005 by Diederik van der Boor
+ email : vdboor --at-- codingdomain.com
+ ***************************************************************************/
+
+#include "service.h"
+#include "xmlfunctions.h"
+
+#include <QDebug>
+#include <QHttp>
+#include <QByteArray>
+
+// This implementation was created with the help of the following documentation:
+// http://www.upnp.org/standardizeddcps/documents/UPnP_IGD_1.0.zip
+// http://zacbowling.com/upnp/
+// http://www.knoxscape.com/Upnp/NAT.htm
+// http://www.artima.com/spontaneous/upnp_digihome2.html
+
+
+namespace UPnP
+{
+
+
+// The constructor for information services
+Service::Service(const QString &hostname, int port, const QString &informationUrl)
+ : informationUrl_(informationUrl)
+ , pendingRequests_(0)
+{
+ http_ = new QHttp(hostname, port);
+ connect(http_, SIGNAL( requestFinished(int,bool) ) ,
+ this, SLOT( slotRequestFinished(int,bool) ) );
+
+qDebug() << "UPnP::Service: Created information service url='" << informationUrl_ << "'." << endl;
+}
+
+
+// The constructor for action services
+Service::Service(const ServiceParameters &params)
+ : controlUrl_(params.controlUrl)
+ , informationUrl_(params.scdpUrl)
+ , pendingRequests_(0)
+ , serviceId_(params.serviceId)
+{
+ http_ = new QHttp(params.hostname, params.port);
+ connect(http_, SIGNAL( requestFinished(int,bool) ) ,
+ this, SLOT( slotRequestFinished(int,bool) ) );
+
+ qDebug() << "CREATED UPnP::Service: url='" << controlUrl_ << "' id='" << serviceId_ << "'." << endl;
+}
+
+
+
+// The destructor
+Service::~Service()
+{
+ qDebug() << "DESTROYED UPnP::Service [url=" << controlUrl_ << ", id=" << serviceId_ << "]" << endl;
+
+ delete http_;
+}
+
+
+
+// Makes a UPnP action request
+// TODO: rename to callMethod / callSoapMethod
+int Service::callAction(const QString &actionName)
+{
+ return callActionInternal(actionName, 0);
+}
+
+
+
+// Makes a UPnP action request
+int Service::callAction(const QString &actionName, const QMap<QString,QString> &arguments)
+{
+ return callActionInternal(actionName, &arguments);
+}
+
+
+
+// Makes a UPnP action request (keeps pointers from the external interface)
+int Service::callActionInternal(const QString &actionName, const QMap<QString,QString> *arguments)
+{
+ qDebug() << "UPnP::Service: calling remote prodecure '" << actionName << "'." << endl;
+
+ // Create the data message
+ QString soapMessage = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\""
+ " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<s:Body>"
+ "<u:" + actionName + " xmlns:u=\"" + serviceId_ + "\">";
+
+ // Do we have any arguments?
+ if(arguments != 0)
+ {
+ // Add the arguments
+ QMap<QString,QString>::const_iterator it;
+ for(it = arguments->begin(); it != arguments->end(); ++it)
+ {
+ QString argumentName = it.key();
+ soapMessage += "<" + argumentName + ">" + it.data() + "</" + argumentName + ">";
+ }
+ }
+
+ // Add the closing tags
+ soapMessage += "</u:" + actionName + "></s:Body></s:Envelope>";
+
+ // Get an utf8 encoding string
+ QByteArray content = soapMessage.utf8();
+
+ // Create the HTTP header
+ QHttpRequestHeader header("POST", controlUrl_);
+ header.setContentType("text/xml; charset=\"utf-8\"");
+ header.setContentLength(content.size());
+ header.setValue("SoapAction", serviceId_ + "#" + actionName);
+
+ // Send the POST request
+ pendingRequests_++;
+ return http_->request(header, content);
+}
+
+
+
+// Makes a UPnP service request
+// TODO: rename to downloadFile()
+int Service::callInformationUrl()
+{
+ qDebug() << "UPnP::Service: requesting file '" << informationUrl_ << "'." << endl;
+
+ // Send the GET request
+ // TODO: User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)
+ pendingRequests_++;
+ return http_->get(informationUrl_);
+}
+
+
+
+// Get the number of pending requests
+int Service::getPendingRequests() const
+{
+ return pendingRequests_;
+}
+
+
+// The control point received an action failure indication
+void Service::gotActionErrorResponse(const QDomNode &response)
+{
+ QString faultString = XmlFunctions::getNodeValue(response, "/faultstring");
+ QString errorCode = XmlFunctions::getNodeValue(response, "/detail/" + faultString + "/errorCode");
+ QString errorDescription = XmlFunctions::getNodeValue(response, "/detail/" + faultString + "/errorDescription");
+ qWarning() << "UPnP::Service - Action failed: " << errorCode << " " << errorDescription << endl;
+}
+
+
+
+// The control point received a response to callAction()
+void Service::gotActionResponse(const QString &responseType, const QMap<QString,QString> &/*resultValues*/)
+{
+ qWarning() << "UPnP::Service - Action response '" << responseType << "' is not handled." << endl;
+}
+
+
+
+// The control point received a response to callInformationUrl()
+void Service::gotInformationResponse(const QDomNode &response)
+{
+ QString rootTagName = response.nodeName();
+ qWarning() << "UPnP::Service - Service response (with root '" << rootTagName << "') is not handled." << endl;
+}
+
+
+
+// The QHttp object retrieved data.
+void Service::slotRequestFinished(int /*id*/, bool error)
+{
+ qDebug() << "UPnP::Service: Got HTTP response." << endl;
+
+ if(! error)
+ {
+ // Not sure why this happens
+ if(http_->bytesAvailable() > 0)
+ {
+ // Get the XML content
+ QByteArray response = http_->readAll();
+ QDomDocument xml;
+
+ // TODO: handle 401 Authorisation required messages
+
+ // Parse the XML
+ QString errorMessage;
+ error = ! xml.setContent(response, false, &errorMessage);
+
+ if(! error)
+ {
+ // Determine how to process the data
+ if(xml.namedItem("s:Envelope").isNull())
+ {
+ qDebug() << "UPnP::Service: Plain XML detected, calling gotInformationResponse()." << endl;
+ // No SOAP envelope found, this is a normal response to callService()
+ gotInformationResponse( xml.lastChild() );
+ }
+ else
+ {
+ qDebug() << xml.toString() << endl;
+ // Got a SOAP message response to callAction()
+ QDomNode resultNode = XmlFunctions::getNode(xml, "/s:Envelope/s:Body").firstChild();
+
+ error = (resultNode.nodeName() == "s:Fault");
+
+ if(! error)
+ {
+ if(resultNode.nodeName().startsWith("m:"))
+ {
+ qDebug() << "UPnP::Service: SOAP Envelope detected, calling gotActionResponse()." << endl;
+ // Action success, return SOAP body
+ QMap<QString,QString> resultValues;
+
+ // Parse all parameters
+ // It's possible to pass the entire QDomNode object to the gotActionResponse()
+ // function, but this is somewhat nicer, and reduces code boat in the subclasses
+ QDomNodeList children = resultNode.childNodes();
+ for(uint i = 0; i < children.count(); i++)
+ {
+ QString key = children.item(i).nodeName();
+ resultValues[ key ] = children.item(i).toElement().text();
+ }
+
+ // Call the gotActionResponse()
+ gotActionResponse(resultNode.nodeName().mid(2), resultValues);
+ }
+ }
+ else
+ {
+ qDebug() << "UPnP::Service: SOAP Error detected, calling gotActionResponse()." << endl;
+
+ // Action failed
+ gotActionErrorResponse(resultNode);
+ }
+ }
+ }
+ else
+ {
+ qWarning() << "UPnP::Service - XML Parsing failed: " << errorMessage << endl;
+ }
+
+ // Only emit when bytes>0
+ pendingRequests_--;
+ emit queryFinished(error);
+ }
+ }
+ else
+ {
+ qWarning() << "UPnP::Service - HTTP Request failed: " << http_->errorString() << endl;
+ pendingRequests_--;
+ emit queryFinished(error);
+ }
+
+}
+
+
+} // end of namespace