summaryrefslogtreecommitdiffstats
path: root/plugins/upnp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 02:37:40 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 02:37:40 +0000
commit9ad5c7b5e23b4940e7a3ea3ca3a6fb77e6a8fab0 (patch)
treed088b5210e77d9fa91d954d8550e00e372b47378 /plugins/upnp
downloadktorrent-9ad5c7b5e23b4940e7a3ea3ca3a6fb77e6a8fab0.tar.gz
ktorrent-9ad5c7b5e23b4940e7a3ea3ca3a6fb77e6a8fab0.zip
Updated to final KDE3 ktorrent release (2.2.6)
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/ktorrent@1077377 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'plugins/upnp')
-rw-r--r--plugins/upnp/Makefile.am38
-rw-r--r--plugins/upnp/ktupnpplugin.desktop26
-rw-r--r--plugins/upnp/ktupnpplugin.kcfg13
-rw-r--r--plugins/upnp/soap.cpp53
-rw-r--r--plugins/upnp/soap.h62
-rw-r--r--plugins/upnp/upnpdescriptionparser.cpp220
-rw-r--r--plugins/upnp/upnpdescriptionparser.h49
-rw-r--r--plugins/upnp/upnpmcastsocket.cpp312
-rw-r--r--plugins/upnp/upnpmcastsocket.h91
-rw-r--r--plugins/upnp/upnpplugin.cpp95
-rw-r--r--plugins/upnp/upnpplugin.h51
-rw-r--r--plugins/upnp/upnppluginsettings.kcfgc7
-rw-r--r--plugins/upnp/upnpprefpage.cpp67
-rw-r--r--plugins/upnp/upnpprefpage.h58
-rw-r--r--plugins/upnp/upnpprefwidget.cpp253
-rw-r--r--plugins/upnp/upnpprefwidget.h83
-rw-r--r--plugins/upnp/upnprouter.cpp459
-rw-r--r--plugins/upnp/upnprouter.h223
-rw-r--r--plugins/upnp/upnpwidget.ui139
19 files changed, 2299 insertions, 0 deletions
diff --git a/plugins/upnp/Makefile.am b/plugins/upnp/Makefile.am
new file mode 100644
index 0000000..8432f90
--- /dev/null
+++ b/plugins/upnp/Makefile.am
@@ -0,0 +1,38 @@
+INCLUDES = -I$(srcdir)/../../libktorrent $(all_includes)
+METASOURCES = AUTO
+
+libktupnp_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libktupnp.la
+libktupnp_la_SOURCES = soap.cpp upnpdescriptionparser.cpp upnpmcastsocket.cpp\
+ upnprouter.cpp
+
+kde_module_LTLIBRARIES = ktupnpplugin.la
+noinst_HEADERS = upnpplugin.h upnpmcastsocket.h upnprouter.h upnpprefpage.h \
+ upnpprefwidget.h upnpdescriptionparser.h soap.h
+ktupnpplugin_la_SOURCES = upnpplugin.cpp upnpprefpage.cpp upnpwidget.ui \
+ upnpprefwidget.cpp upnppluginsettings.kcfgc
+
+# Libs needed by the plugin
+ktupnpplugin_la_LIBADD = libktupnp.la \
+ $(LIB_KPARTS) ../../libktorrent/libktorrent.la \
+ $(LIB_QT) \
+ $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_KFILE)
+
+
+
+# LD flags for the plugin
+# -module says: this is a module, i.e. something you're going to dlopen
+# so e.g. it has no version number like a normal shared lib would have.
+ktupnpplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+
+# rc file containing the GUI for the plugin
+# pluginsdir = $(kde_datadir)/ktsearchplugin
+# plugins_DATA = ktsearchpluginui.rc
+
+# Install the desktop file needed to detect the plugin
+kde_services_DATA = ktupnpplugin.desktop
+
+kde_kcfg_DATA = ktupnpplugin.kcfg
+
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/plugins/upnp/ktupnpplugin.desktop b/plugins/upnp/ktupnpplugin.desktop
new file mode 100644
index 0000000..67948d7
--- /dev/null
+++ b/plugins/upnp/ktupnpplugin.desktop
@@ -0,0 +1,26 @@
+[Desktop Entry]
+Name=UPnPPlugin
+Name[bg]=Приставка UPnP
+Name[br]=Lugent UPnP
+Name[de]=UPnP-Modul
+Name[el]=Πρόσθετο UPnP
+Name[es]=Complemento UPnP
+Name[et]=UPnP plugin
+Name[it]=Plugin UPnP
+Name[nb]=UPnP-modul
+Name[nds]=UPnP-Moduul
+Name[nl]=UPnP-plugin
+Name[pl]=Wtyczka UPnP
+Name[pt]='Plugin' UPnP
+Name[pt_BR]=Plugin UPnP
+Name[sk]=UPnP Plugin
+Name[sr]=Прикључак за UPnP
+Name[sr@Latn]=Priključak za UPnP
+Name[sv]=UPnP-insticksprogram
+Name[tr]=UPnP Eklentisi
+Name[xx]=xxUPnPPluginxx
+Name[zh_CN]=UPnP 插件
+Name[zh_TW]=UPnP外掛程式
+ServiceTypes=KTorrent/Plugin
+Type=Service
+X-KDE-Library=ktupnpplugin
diff --git a/plugins/upnp/ktupnpplugin.kcfg b/plugins/upnp/ktupnpplugin.kcfg
new file mode 100644
index 0000000..acce783
--- /dev/null
+++ b/plugins/upnp/ktupnpplugin.kcfg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+
+ <kcfgfile name="ktupnppluginrc"/>
+ <group name="general">
+ <entry name="defaultDevice" type="String">
+ <label>Default UPnP device to use</label>
+ </entry>
+ </group>
+</kcfg>
diff --git a/plugins/upnp/soap.cpp b/plugins/upnp/soap.cpp
new file mode 100644
index 0000000..b155b55
--- /dev/null
+++ b/plugins/upnp/soap.cpp
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include "soap.h"
+
+namespace kt
+{
+
+ QString SOAP::createCommand(const QString & action,const QString & service)
+ {
+ QString comm = QString("<?xml version=\"1.0\"?>\r\n"
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<SOAP-ENV:Body>"
+ "<m:%1 xmlns:m=\"%2\"/>"
+ "</SOAP-ENV:Body></SOAP-ENV:Envelope>"
+ "\r\n").arg(action).arg(service);
+
+ return comm;
+ }
+
+ QString SOAP::createCommand(const QString & action,const QString & service,const QValueList<Arg> & args)
+ {
+ QString comm = QString("<?xml version=\"1.0\"?>\r\n"
+ "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ "<SOAP-ENV:Body>"
+ "<m:%1 xmlns:m=\"%2\">").arg(action).arg(service);
+
+ for (QValueList<Arg>::const_iterator i = args.begin();i != args.end();i++)
+ {
+ const Arg & a = *i;
+ comm += "<" + a.element + ">" + a.value + "</" + a.element + ">";
+ }
+
+ comm += QString("</m:%1></SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n").arg(action);
+ return comm;
+ }
+}
diff --git a/plugins/upnp/soap.h b/plugins/upnp/soap.h
new file mode 100644
index 0000000..c11e2ed
--- /dev/null
+++ b/plugins/upnp/soap.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTSOAP_H
+#define KTSOAP_H
+
+#include <qvaluelist.h>
+#include <qstring.h>
+
+namespace kt
+{
+
+ /**
+ @author Joris Guisson
+ */
+ class SOAP
+ {
+ public:
+
+ /**
+ * Create a simple UPnP SOAP command without parameters.
+ * @param action The name of the action
+ * @param service The name of the service
+ * @return The command
+ */
+ static QString createCommand(const QString & action,const QString & service);
+
+ struct Arg
+ {
+ QString element;
+ QString value;
+ };
+
+ /**
+ * Create a UPnP SOAP command with parameters.
+ * @param action The name of the action
+ * @param service The name of the service
+ * @param args Arguments for command
+ * @return The command
+ */
+ static QString createCommand(const QString & action,const QString & service,const QValueList<Arg> & args);
+ };
+
+}
+
+#endif
diff --git a/plugins/upnp/upnpdescriptionparser.cpp b/plugins/upnp/upnpdescriptionparser.cpp
new file mode 100644
index 0000000..43afbc3
--- /dev/null
+++ b/plugins/upnp/upnpdescriptionparser.cpp
@@ -0,0 +1,220 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <qxml.h>
+#include <qvaluestack.h>
+#include <util/fileops.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include "upnprouter.h"
+#include "upnpdescriptionparser.h"
+
+using namespace bt;
+
+namespace kt
+{
+
+ class XMLContentHandler : public QXmlDefaultHandler
+ {
+ enum Status
+ {
+ TOPLEVEL,ROOT,DEVICE,SERVICE,FIELD,OTHER
+ };
+
+ QString tmp;
+ UPnPRouter* router;
+ UPnPService curr_service;
+ QValueStack<Status> status_stack;
+ public:
+ XMLContentHandler(UPnPRouter* router);
+ virtual ~XMLContentHandler();
+
+
+ bool startDocument();
+ bool endDocument();
+ bool startElement(const QString &, const QString & localName, const QString &,
+ const QXmlAttributes & atts);
+ bool endElement(const QString & , const QString & localName, const QString & );
+ bool characters(const QString & ch);
+
+ bool interestingDeviceField(const QString & name);
+ bool interestingServiceField(const QString & name);
+ };
+
+
+ UPnPDescriptionParser::UPnPDescriptionParser()
+ {}
+
+
+ UPnPDescriptionParser::~UPnPDescriptionParser()
+ {}
+
+ bool UPnPDescriptionParser::parse(const QString & file,UPnPRouter* router)
+ {
+ bool ret = true;
+ {
+ QFile fptr(file);
+ if (!fptr.open(IO_ReadOnly))
+ return false;
+
+ QXmlInputSource input(&fptr);
+ XMLContentHandler chandler(router);
+ QXmlSimpleReader reader;
+
+ reader.setContentHandler(&chandler);
+ ret = reader.parse(&input,false);
+ }
+
+ if (!ret)
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "Error parsing XML" << endl;
+ return false;
+ }
+ return true;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+
+
+ XMLContentHandler::XMLContentHandler(UPnPRouter* router) : router(router)
+ {}
+
+ XMLContentHandler::~XMLContentHandler()
+ {}
+
+
+ bool XMLContentHandler::startDocument()
+ {
+ status_stack.push(TOPLEVEL);
+ return true;
+ }
+
+ bool XMLContentHandler::endDocument()
+ {
+ status_stack.pop();
+ return true;
+ }
+
+ bool XMLContentHandler::interestingDeviceField(const QString & name)
+ {
+ return name == "friendlyName" || name == "manufacturer" || name == "modelDescription" ||
+ name == "modelName" || name == "modelNumber";
+ }
+
+
+ bool XMLContentHandler::interestingServiceField(const QString & name)
+ {
+ return name == "serviceType" || name == "serviceId" || name == "SCPDURL" ||
+ name == "controlURL" || name == "eventSubURL";
+ }
+
+ bool XMLContentHandler::startElement(const QString &, const QString & localName, const QString &,
+ const QXmlAttributes & )
+ {
+ tmp = "";
+ switch (status_stack.top())
+ {
+ case TOPLEVEL:
+ // from toplevel we can only go to root
+ if (localName == "root")
+ status_stack.push(ROOT);
+ else
+ return false;
+ break;
+ case ROOT:
+ // from the root we can go to device or specVersion
+ // we are not interested in the specVersion
+ if (localName == "device")
+ status_stack.push(DEVICE);
+ else
+ status_stack.push(OTHER);
+ break;
+ case DEVICE:
+ // see if it is a field we are interested in
+ if (interestingDeviceField(localName))
+ status_stack.push(FIELD);
+ else
+ status_stack.push(OTHER);
+ break;
+ case SERVICE:
+ if (interestingServiceField(localName))
+ status_stack.push(FIELD);
+ else
+ status_stack.push(OTHER);
+ break;
+ case OTHER:
+ if (localName == "service")
+ status_stack.push(SERVICE);
+ else if (localName == "device")
+ status_stack.push(DEVICE);
+ else
+ status_stack.push(OTHER);
+ break;
+ case FIELD:
+ break;
+ }
+ return true;
+ }
+
+ bool XMLContentHandler::endElement(const QString & , const QString & localName, const QString & )
+ {
+ switch (status_stack.top())
+ {
+ case FIELD:
+ // we have a field so set it
+ status_stack.pop();
+ if (status_stack.top() == DEVICE)
+ {
+ // if we are in a device
+ router->getDescription().setProperty(localName,tmp);
+ }
+ else if (status_stack.top() == SERVICE)
+ {
+ // set a property of a service
+ curr_service.setProperty(localName,tmp);
+ }
+ break;
+ case SERVICE:
+ // add the service
+ router->addService(curr_service);
+ curr_service.clear();
+ // pop the stack
+ status_stack.pop();
+ break;
+ default:
+ status_stack.pop();
+ break;
+ }
+
+ // reset tmp
+ tmp = "";
+ return true;
+ }
+
+
+ bool XMLContentHandler::characters(const QString & ch)
+ {
+ if (ch.length() > 0)
+ {
+ tmp += ch;
+ }
+ return true;
+ }
+
+}
diff --git a/plugins/upnp/upnpdescriptionparser.h b/plugins/upnp/upnpdescriptionparser.h
new file mode 100644
index 0000000..5d4bf1e
--- /dev/null
+++ b/plugins/upnp/upnpdescriptionparser.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTUPNPDESCRIPTIONPARSER_H
+#define KTUPNPDESCRIPTIONPARSER_H
+
+namespace kt
+{
+ class UPnPRouter;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Parses the xml description of a router.
+ */
+ class UPnPDescriptionParser
+ {
+ public:
+ UPnPDescriptionParser();
+ virtual ~UPnPDescriptionParser();
+
+ /**
+ * Parse the xml description.
+ * @param file File it is located in
+ * @param router The router off the xml description
+ * @return true upon success
+ */
+ bool parse(const QString & file,UPnPRouter* router);
+ };
+
+}
+
+#endif
diff --git a/plugins/upnp/upnpmcastsocket.cpp b/plugins/upnp/upnpmcastsocket.cpp
new file mode 100644
index 0000000..47712ea
--- /dev/null
+++ b/plugins/upnp/upnpmcastsocket.cpp
@@ -0,0 +1,312 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <kurl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <qstringlist.h>
+#include <ksocketdevice.h>
+#include <ksocketaddress.h>
+#include <util/log.h>
+#include <torrent/globals.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include "upnpmcastsocket.h"
+
+
+
+using namespace KNetwork;
+using namespace bt;
+
+namespace kt
+{
+
+ UPnPMCastSocket::UPnPMCastSocket(bool verbose) : verbose(verbose)
+ {
+ routers.setAutoDelete(true);
+ QObject::connect(this,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
+ QObject::connect(this,SIGNAL(gotError(int)),this,SLOT(onError(int)));
+ setAddressReuseable(true);
+ setFamily(KNetwork::KResolver::IPv4Family);
+ setBlocking(true);
+ for (Uint32 i = 0;i < 10;i++)
+ {
+ if (!bind(QString::null,QString::number(1900 + i)))
+ Out(SYS_PNP|LOG_IMPORTANT) << "Cannot bind to UDP port 1900" << endl;
+ else
+ break;
+ }
+ setBlocking(false);
+ joinUPnPMCastGroup();
+ }
+
+
+ UPnPMCastSocket::~UPnPMCastSocket()
+ {
+ leaveUPnPMCastGroup();
+ QObject::disconnect(this,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
+ QObject::disconnect(this,SIGNAL(gotError(int)),this,SLOT(onError(int)));
+ }
+
+ void UPnPMCastSocket::discover()
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Trying to find UPnP devices on the local network" << endl;
+
+ // send a HTTP M-SEARCH message to 239.255.255.250:1900
+ const char* data = "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: 239.255.255.250:1900\r\n"
+ "ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
+ "MAN:\"ssdp:discover\"\r\n"
+ "MX:3\r\n"
+ "\r\n\0";
+
+ if (verbose)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Sending : " << endl;
+ Out(SYS_PNP|LOG_NOTICE) << data << endl;
+ }
+
+ KDatagramSocket::send(KNetwork::KDatagramPacket(data,strlen(data),KInetSocketAddress("239.255.255.250",1900)));
+ }
+
+ void UPnPMCastSocket::onXmlFileDownloaded(UPnPRouter* r,bool success)
+ {
+ if (!success)
+ {
+ // we couldn't download and parse the XML file so
+ // get rid of it
+ r->deleteLater();
+ }
+ else
+ {
+ // add it to the list and emit the signal
+ if (!routers.contains(r->getServer()))
+ {
+ routers.insert(r->getServer(),r);
+ discovered(r);
+ }
+ else
+ {
+ r->deleteLater();
+ }
+ }
+ }
+
+ void UPnPMCastSocket::onReadyRead()
+ {
+ if (bytesAvailable() == 0)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "0 byte UDP packet " << endl;
+ // KDatagramSocket wrongly handles UDP packets with no payload
+ // so we need to deal with it oursleves
+ int fd = socketDevice()->socket();
+ char tmp;
+ read(fd,&tmp,1);
+ return;
+ }
+
+ KNetwork::KDatagramPacket p = KDatagramSocket::receive();
+ if (p.isNull())
+ return;
+
+ if (verbose)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Received : " << endl;
+ Out(SYS_PNP|LOG_NOTICE) << QString(p.data()) << endl;
+ }
+
+ // try to make a router of it
+ UPnPRouter* r = parseResponse(p.data());
+ if (r)
+ {
+ QObject::connect(r,SIGNAL(xmlFileDownloaded( UPnPRouter*, bool )),
+ this,SLOT(onXmlFileDownloaded( UPnPRouter*, bool )));
+
+ // download it's xml file
+ r->downloadXMLFile();
+ }
+ }
+
+ UPnPRouter* UPnPMCastSocket::parseResponse(const QByteArray & arr)
+ {
+ QStringList lines = QStringList::split("\r\n",QString(arr),false);
+ QString server;
+ KURL location;
+
+ /*
+ Out(SYS_PNP|LOG_DEBUG) << "Received : " << endl;
+ for (Uint32 idx = 0;idx < lines.count(); idx++)
+ Out(SYS_PNP|LOG_DEBUG) << lines[idx] << endl;
+ */
+
+ // first read first line and see if contains a HTTP 200 OK message
+ QString line = lines.first();
+ if (!line.contains("HTTP"))
+ {
+ // it is either a 200 OK or a NOTIFY
+ if (!line.contains("NOTIFY") && !line.contains("200"))
+ return 0;
+ }
+ else if (line.contains("M-SEARCH")) // ignore M-SEARCH
+ return 0;
+
+ // quick check that the response being parsed is valid
+ bool validDevice = false;
+ for (Uint32 idx = 0;idx < lines.count() && !validDevice; idx++)
+ {
+ line = lines[idx];
+ if ((line.contains("ST:") || line.contains("NT:")) && line.contains("InternetGatewayDevice"))
+ {
+ validDevice = true;
+ }
+ }
+ if (!validDevice)
+ {
+ // Out(SYS_PNP|LOG_IMPORTANT) << "Not a valid Internet Gateway Device" << endl;
+ return 0;
+ }
+
+ // read all lines and try to find the server and location fields
+ for (Uint32 i = 1;i < lines.count();i++)
+ {
+ line = lines[i];
+ if (line.startsWith("Location") || line.startsWith("LOCATION") || line.startsWith("location"))
+ {
+ location = line.mid(line.find(':') + 1).stripWhiteSpace();
+ if (!location.isValid())
+ return 0;
+ }
+ else if (line.startsWith("Server") || line.startsWith("server") || line.startsWith("SERVER"))
+ {
+ server = line.mid(line.find(':') + 1).stripWhiteSpace();
+ if (server.length() == 0)
+ return 0;
+
+ }
+ }
+
+ if (routers.contains(server))
+ {
+ return 0;
+ }
+ else
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Detected IGD " << server << endl;
+ // everything OK, make a new UPnPRouter
+ return new UPnPRouter(server,location,verbose);
+ }
+ }
+
+ void UPnPMCastSocket::onError(int)
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "UPnPMCastSocket Error : " << errorString() << endl;
+ }
+
+ void UPnPMCastSocket::saveRouters(const QString & file)
+ {
+ QFile fptr(file);
+ if (!fptr.open(IO_WriteOnly))
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "Cannot open file " << file << " : " << fptr.errorString() << endl;
+ return;
+ }
+
+ // file format is simple : 2 lines per router,
+ // one containing the server, the other the location
+ QTextStream fout(&fptr);
+ bt::PtrMap<QString,UPnPRouter>::iterator i = routers.begin();
+ while (i != routers.end())
+ {
+ UPnPRouter* r = i->second;
+ fout << r->getServer() << endl;
+ fout << r->getLocation().prettyURL() << endl;
+ i++;
+ }
+ }
+
+ void UPnPMCastSocket::loadRouters(const QString & file)
+ {
+ QFile fptr(file);
+ if (!fptr.open(IO_ReadOnly))
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "Cannot open file " << file << " : " << fptr.errorString() << endl;
+ return;
+ }
+
+ // file format is simple : 2 lines per router,
+ // one containing the server, the other the location
+ QTextStream fin(&fptr);
+
+ while (!fin.atEnd())
+ {
+ QString server, location;
+ server = fin.readLine();
+ location = fin.readLine();
+ if (!routers.contains(server))
+ {
+ UPnPRouter* r = new UPnPRouter(server,location);
+ // download it's xml file
+ QObject::connect(r,SIGNAL(xmlFileDownloaded( UPnPRouter*, bool )),this,SLOT(onXmlFileDownloaded( UPnPRouter*, bool )));
+ r->downloadXMLFile();
+ }
+ }
+ }
+
+ void UPnPMCastSocket::joinUPnPMCastGroup()
+ {
+ int fd = socketDevice()->socket();
+ struct ip_mreq mreq;
+
+ memset(&mreq,0,sizeof(struct ip_mreq));
+
+ inet_aton("239.255.255.250",&mreq.imr_multiaddr);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+
+ if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(struct ip_mreq)) < 0)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Failed to join multicast group 239.255.255.250" << endl;
+ }
+ }
+
+ void UPnPMCastSocket::leaveUPnPMCastGroup()
+ {
+ int fd = socketDevice()->socket();
+ struct ip_mreq mreq;
+
+ memset(&mreq,0,sizeof(struct ip_mreq));
+
+ inet_aton("239.255.255.250",&mreq.imr_multiaddr);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+
+ if (setsockopt(fd,IPPROTO_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(struct ip_mreq)) < 0)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Failed to leave multicast group 239.255.255.250" << endl;
+ }
+ }
+}
+
+
+
+#include "upnpmcastsocket.moc"
diff --git a/plugins/upnp/upnpmcastsocket.h b/plugins/upnp/upnpmcastsocket.h
new file mode 100644
index 0000000..493c5b9
--- /dev/null
+++ b/plugins/upnp/upnpmcastsocket.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTUPNPMCASTSOCKET_H
+#define KTUPNPMCASTSOCKET_H
+
+#include <util/ptrmap.h>
+#include <kdatagramsocket.h>
+#include <util/constants.h>
+#include "upnprouter.h"
+
+using bt::Uint32;
+
+namespace kt
+{
+ class UPnPRouter;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Socket used to discover UPnP devices. This class will keep track
+ * of all discovered devices.
+ */
+ class UPnPMCastSocket : public KNetwork::KDatagramSocket
+ {
+ Q_OBJECT
+ public:
+ UPnPMCastSocket(bool verbose = false);
+ virtual ~UPnPMCastSocket();
+
+ /// Get the number of routers discovered
+ Uint32 getNumDevicesDiscovered() const {return routers.count();}
+
+ /// Find a router using it's server name
+ UPnPRouter* findDevice(const QString & name) {return routers.find(name);}
+
+ /// Save all routers to a file (for convenience at startup)
+ void saveRouters(const QString & file);
+
+ /// Load all routers from a file
+ void loadRouters(const QString & file);
+
+ public slots:
+ /**
+ * Try to discover a UPnP device on the network.
+ * A signal will be emitted when a device is found.
+ */
+ void discover();
+
+ private slots:
+ void onReadyRead();
+ void onError(int);
+ void onXmlFileDownloaded(UPnPRouter* r,bool success);
+
+ signals:
+ /**
+ * Emitted when a router or internet gateway device is detected.
+ * @param router The router
+ */
+ void discovered(UPnPRouter* router);
+
+ public:
+ UPnPRouter* parseResponse(const QByteArray & arr);
+
+ private:
+ void joinUPnPMCastGroup();
+ void leaveUPnPMCastGroup();
+
+ private:
+ bt::PtrMap<QString,UPnPRouter> routers;
+ bool verbose;
+ };
+}
+
+#endif
diff --git a/plugins/upnp/upnpplugin.cpp b/plugins/upnp/upnpplugin.cpp
new file mode 100644
index 0000000..dbe58b4
--- /dev/null
+++ b/plugins/upnp/upnpplugin.cpp
@@ -0,0 +1,95 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <kgenericfactory.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+#include <kstdaction.h>
+#include <kpopupmenu.h>
+#include <interfaces/guiinterface.h>
+#include <util/fileops.h>
+#include "upnpplugin.h"
+#include "upnpmcastsocket.h"
+#include "upnpprefpage.h"
+
+
+#define NAME "UPnP"
+#define AUTHOR "Joris Guisson"
+#define EMAIL "joris.guisson@gmail.com"
+
+
+
+K_EXPORT_COMPONENT_FACTORY(ktupnpplugin,KGenericFactory<kt::UPnPPlugin>("ktupnpplugin"))
+
+namespace kt
+{
+
+ UPnPPlugin::UPnPPlugin(QObject* parent, const char* name, const QStringList& args)
+ : Plugin(parent, name, args,NAME,i18n("UPnP"),AUTHOR,EMAIL,i18n("Uses UPnP to automatically forward ports on your router"),"ktupnp")
+ {
+ sock = 0;
+ pref = 0;
+ }
+
+
+ UPnPPlugin::~UPnPPlugin()
+ {
+ delete sock;
+ delete pref;
+ }
+
+
+ void UPnPPlugin::load()
+ {
+ //KIconLoader* iload = KGlobal::iconLoader();
+ sock = new UPnPMCastSocket();
+ pref = new UPnPPrefPage(sock);
+ this->getGUI()->addPrefPage(pref);
+ // load the routers list
+ QString routers_file = KGlobal::dirs()->saveLocation("data","ktorrent") + "routers";
+ if (bt::Exists(routers_file))
+ sock->loadRouters(routers_file);
+ sock->discover();
+ }
+
+ void UPnPPlugin::unload()
+ {
+ QString routers_file = KGlobal::dirs()->saveLocation("data","ktorrent") + "routers";
+ sock->saveRouters(routers_file);
+ this->getGUI()->removePrefPage(pref);
+ sock->close();
+ delete pref;
+ pref = 0;
+ delete sock;
+ sock = 0;
+ }
+
+ void UPnPPlugin::shutdown(bt::WaitJob* job)
+ {
+ pref->shutdown(job);
+ }
+
+ bool UPnPPlugin::versionCheck(const QString & version) const
+ {
+ return version == KT_VERSION_MACRO;
+ }
+}
+#include "upnpplugin.moc"
diff --git a/plugins/upnp/upnpplugin.h b/plugins/upnp/upnpplugin.h
new file mode 100644
index 0000000..a6ca78a
--- /dev/null
+++ b/plugins/upnp/upnpplugin.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTSEARCHPLUGIN_H
+#define KTSEARCHPLUGIN_H
+
+#include <interfaces/plugin.h>
+
+namespace kt
+{
+ class UPnPMCastSocket;
+ class UPnPPrefPage;
+
+ /**
+ @author Joris Guisson
+ */
+ class UPnPPlugin : public Plugin
+ {
+ Q_OBJECT
+ public:
+ UPnPPlugin(QObject* parent, const char* name, const QStringList& args);
+ virtual ~UPnPPlugin();
+
+ virtual void load();
+ virtual void unload();
+ virtual void shutdown(bt::WaitJob* job);
+ virtual bool versionCheck(const QString& version) const;
+ private:
+ UPnPMCastSocket* sock;
+ UPnPPrefPage* pref;
+ };
+
+}
+
+#endif
diff --git a/plugins/upnp/upnppluginsettings.kcfgc b/plugins/upnp/upnppluginsettings.kcfgc
new file mode 100644
index 0000000..6cab465
--- /dev/null
+++ b/plugins/upnp/upnppluginsettings.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=ktupnpplugin.kcfg
+ClassName=UPnPPluginSettings
+Namespace=kt
+Singleton=true
+Mutators=true
+# will create the necessary code for setting those variables
diff --git a/plugins/upnp/upnpprefpage.cpp b/plugins/upnp/upnpprefpage.cpp
new file mode 100644
index 0000000..dc50c2f
--- /dev/null
+++ b/plugins/upnp/upnpprefpage.cpp
@@ -0,0 +1,67 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include "upnpprefpage.h"
+#include "upnpprefwidget.h"
+#include "upnprouter.h"
+#include "upnpmcastsocket.h"
+
+namespace kt
+{
+
+ UPnPPrefPage::UPnPPrefPage(UPnPMCastSocket* sock): PrefPageInterface(i18n("UPnP"), i18n("UPnP Devices"),KGlobal::iconLoader()->loadIcon("ktupnp",KIcon::NoGroup)),sock(sock)
+ {
+ widget = 0;
+ }
+
+
+ UPnPPrefPage::~UPnPPrefPage()
+ {}
+
+
+ bool UPnPPrefPage::apply()
+ {
+ return true;
+ }
+
+ void UPnPPrefPage::createWidget(QWidget* parent)
+ {
+ widget = new UPnPPrefWidget(parent);
+ QObject::connect(sock,SIGNAL(discovered(UPnPRouter* )),widget,SLOT(addDevice(UPnPRouter* )));
+ QObject::connect(widget,SIGNAL(rescan()),sock,SLOT(discover()));
+ }
+
+ void UPnPPrefPage::deleteWidget()
+ {
+ delete widget;
+ widget = 0;
+ }
+
+ void UPnPPrefPage::updateData()
+ {
+ }
+
+ void UPnPPrefPage::shutdown(bt::WaitJob* job)
+ {
+ widget->shutdown(job);
+ }
+}
diff --git a/plugins/upnp/upnpprefpage.h b/plugins/upnp/upnpprefpage.h
new file mode 100644
index 0000000..7d5b4f5
--- /dev/null
+++ b/plugins/upnp/upnpprefpage.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTUPNPPREFPAGE_H
+#define KTUPNPPREFPAGE_H
+
+#include <interfaces/prefpageinterface.h>
+
+namespace bt
+{
+ class WaitJob;
+}
+
+namespace kt
+{
+ class UPnPMCastSocket;
+ class UPnPPrefWidget;
+
+ /**
+ * @author Joris Guisson
+ *
+ * Page in the preference dialog for the UPnP plugin.
+ */
+ class UPnPPrefPage : public PrefPageInterface
+ {
+ UPnPMCastSocket* sock;
+ UPnPPrefWidget* widget;
+ public:
+ UPnPPrefPage(UPnPMCastSocket* sock);
+ virtual ~UPnPPrefPage();
+
+ virtual bool apply();
+ virtual void createWidget(QWidget* parent);
+ virtual void deleteWidget();
+ virtual void updateData();
+
+ void shutdown(bt::WaitJob* job);
+ };
+
+}
+
+#endif
diff --git a/plugins/upnp/upnpprefwidget.cpp b/plugins/upnp/upnpprefwidget.cpp
new file mode 100644
index 0000000..43e2aec
--- /dev/null
+++ b/plugins/upnp/upnpprefwidget.cpp
@@ -0,0 +1,253 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include <klistview.h>
+#include <kmessagebox.h>
+#include <kpushbutton.h>
+#include <torrent/udptrackersocket.h>
+#include <torrent/globals.h>
+#include <torrent/server.h>
+#include <kademlia/dhtbase.h>
+#include "upnpprefwidget.h"
+#include <util/log.h>
+#include <util/error.h>
+#include <util/waitjob.h>
+#include <util/httprequest.h>
+#include <torrent/globals.h>
+#include "upnppluginsettings.h"
+
+using namespace bt;
+
+namespace kt
+{
+ UPnPPrefWidget::UPnPPrefWidget(QWidget* parent, const char* name, WFlags fl)
+ : UPnPWidget(parent,name,fl)
+ {
+ def_router = 0;
+ connect(m_forward_btn,SIGNAL(clicked()),this,SLOT(onForwardBtnClicked()));
+ connect(m_undo_forward_btn,SIGNAL(clicked()),this,SLOT(onUndoForwardBtnClicked()));
+ connect(m_rescan,SIGNAL(clicked()),this,SLOT(onRescanClicked()));
+ bt::Globals::instance().getPortList().setListener(this);
+ }
+
+ UPnPPrefWidget::~UPnPPrefWidget()
+ {
+ bt::Globals::instance().getPortList().setListener(0);
+ }
+
+ void UPnPPrefWidget::shutdown(bt::WaitJob* job)
+ {
+ if (!def_router)
+ return;
+
+ net::PortList & pl = bt::Globals::instance().getPortList();
+ if (pl.count() == 0)
+ return;
+
+ for (net::PortList::iterator i = pl.begin(); i != pl.end();i++)
+ {
+ net::Port & p = *i;
+ if (p.forward)
+ def_router->undoForward(p,job);
+ }
+ }
+
+
+ void UPnPPrefWidget::addDevice(UPnPRouter* r)
+ {
+ connect(r,SIGNAL(updateGUI()),this,SLOT(updatePortMappings()));
+ KListViewItem* item = new KListViewItem(m_device_list,r->getDescription().friendlyName);
+ item->setMultiLinesEnabled(true);
+ itemmap[item] = r;
+ // if we have discovered the default device or there is none
+ // forward it's ports
+ QString def_dev = UPnPPluginSettings::defaultDevice();
+ if (def_dev == r->getServer() || def_dev.length() == 0)
+ {
+ Out(SYS_PNP|LOG_DEBUG) << "Doing default port mappings ..." << endl;
+ UPnPPluginSettings::setDefaultDevice(r->getServer());
+ UPnPPluginSettings::writeConfig();
+
+ try
+ {
+ net::PortList & pl = bt::Globals::instance().getPortList();
+
+ for (net::PortList::iterator i = pl.begin(); i != pl.end();i++)
+ {
+ net::Port & p = *i;
+ if (p.forward)
+ r->forward(p);
+ }
+
+ def_router = r;
+ }
+ catch (Error & e)
+ {
+ KMessageBox::error(this,e.toString());
+ }
+ }
+ }
+
+ void UPnPPrefWidget::onForwardBtnClicked()
+ {
+ KListViewItem* item = (KListViewItem*)m_device_list->currentItem();;
+ if (!item)
+ return;
+
+ UPnPRouter* r = itemmap[item];
+ if (!r)
+ return;
+
+ try
+ {
+ net::PortList & pl = bt::Globals::instance().getPortList();
+
+ for (net::PortList::iterator i = pl.begin(); i != pl.end();i++)
+ {
+ net::Port & p = *i;
+ if (p.forward)
+ r->forward(p);
+ }
+
+ QString def_dev = UPnPPluginSettings::defaultDevice();
+ if (def_dev != r->getServer())
+ {
+ UPnPPluginSettings::setDefaultDevice(r->getServer());
+ UPnPPluginSettings::writeConfig();
+ def_router = r;
+ }
+
+ }
+ catch (Error & e)
+ {
+ KMessageBox::error(this,e.toString());
+ }
+ }
+
+ void UPnPPrefWidget::onRescanClicked()
+ {
+ // clear the list and emit the signal
+ rescan();
+ }
+
+ void UPnPPrefWidget::onUndoForwardBtnClicked()
+ {
+ KListViewItem* item = (KListViewItem*)m_device_list->currentItem();;
+ if (!item)
+ return;
+
+ UPnPRouter* r = itemmap[item];
+ if (!r)
+ return;
+
+ try
+ {
+ net::PortList & pl = bt::Globals::instance().getPortList();
+
+ for (net::PortList::iterator i = pl.begin(); i != pl.end();i++)
+ {
+ net::Port & p = *i;
+ if (p.forward)
+ r->undoForward(p,false);
+ }
+
+ QString def_dev = UPnPPluginSettings::defaultDevice();
+ if (def_dev == r->getServer())
+ {
+ UPnPPluginSettings::setDefaultDevice(QString::null);
+ UPnPPluginSettings::writeConfig();
+ def_router = 0;
+ }
+ }
+ catch (Error & e)
+ {
+ KMessageBox::error(this,e.toString());
+ }
+ }
+
+
+ void UPnPPrefWidget::updatePortMappings()
+ {
+ // update all port mappings
+ QMap<KListViewItem*,UPnPRouter*>::iterator i = itemmap.begin();
+ while (i != itemmap.end())
+ {
+ UPnPRouter* r = i.data();
+ KListViewItem* item = i.key();
+ QString msg,services;
+ QValueList<UPnPRouter::Forwarding>::iterator j = r->beginPortMappings();
+ while (j != r->endPortMappings())
+ {
+ UPnPRouter::Forwarding & f = *j;
+ if (!f.pending_req)
+ {
+ msg += QString::number(f.port.number) + " (";
+ QString prot = (f.port.proto == net::UDP ? "UDP" : "TCP");
+ msg += prot + ")";
+ if (f.service->servicetype.contains("WANPPPConnection"))
+ services += "PPP";
+ else
+ services += "IP";
+ }
+ j++;
+ if (j != r->endPortMappings())
+ {
+ msg += "\n";
+ services += "\n";
+ }
+ }
+ item->setText(1,msg);
+ item->setText(2,services);
+ i++;
+ }
+ }
+
+
+ void UPnPPrefWidget::portAdded(const net::Port & port)
+ {
+ try
+ {
+ if (def_router && port.forward)
+ def_router->forward(port);
+ }
+ catch (Error & e)
+ {
+ Out(SYS_PNP|LOG_DEBUG) << "Error : " << e.toString() << endl;
+ }
+ }
+
+ void UPnPPrefWidget::portRemoved(const net::Port & port)
+ {
+ try
+ {
+ if (def_router && port.forward)
+ def_router->undoForward(port,false);
+ }
+ catch (Error & e)
+ {
+ Out(SYS_PNP|LOG_DEBUG) << "Error : " << e.toString() << endl;
+ }
+ }
+}
+
+
+
+#include "upnpprefwidget.moc"
+
diff --git a/plugins/upnp/upnpprefwidget.h b/plugins/upnp/upnpprefwidget.h
new file mode 100644
index 0000000..16fa31b
--- /dev/null
+++ b/plugins/upnp/upnpprefwidget.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef UPNPPREFWIDGET_H
+#define UPNPPREFWIDGET_H
+
+#include <qmap.h>
+#include "upnprouter.h"
+#include "upnpwidget.h"
+
+class KListViewItem;
+
+namespace bt
+{
+ class WaitJob;
+}
+
+namespace kt
+{
+
+ /**
+ * Widget for the UPnP pref dialog page.
+ */
+ class UPnPPrefWidget : public UPnPWidget,public net::PortListener
+ {
+ Q_OBJECT
+
+ public:
+ UPnPPrefWidget(QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
+ virtual ~UPnPPrefWidget();
+
+ void shutdown(bt::WaitJob* job);
+
+
+ public slots:
+ /**
+ * Add a device to the list.
+ * @param r The device
+ */
+ void addDevice(UPnPRouter* r);
+
+ signals:
+ /**
+ * Emitted when the user presses the rescan button.
+ */
+ void rescan();
+
+
+ protected slots:
+ void onForwardBtnClicked();
+ void onUndoForwardBtnClicked();
+ void onRescanClicked();
+ void updatePortMappings();
+
+ private:
+ virtual void portAdded(const net::Port & port);
+ virtual void portRemoved(const net::Port & port);
+
+ private:
+ QMap<KListViewItem*,UPnPRouter*> itemmap;
+ UPnPRouter* def_router;
+ };
+}
+
+#endif
+
diff --git a/plugins/upnp/upnprouter.cpp b/plugins/upnp/upnprouter.cpp
new file mode 100644
index 0000000..617abf5
--- /dev/null
+++ b/plugins/upnp/upnprouter.cpp
@@ -0,0 +1,459 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <qstringlist.h>
+#include <kio/netaccess.h>
+#include <kio/job.h>
+#include <torrent/globals.h>
+#include <util/log.h>
+#include <util/array.h>
+#include <util/error.h>
+#include <util/functions.h>
+#include <util/fileops.h>
+#include <util/httprequest.h>
+#include <util/waitjob.h>
+#include "upnprouter.h"
+#include "upnpdescriptionparser.h"
+#include "soap.h"
+
+using namespace bt;
+using namespace net;
+
+namespace kt
+{
+ UPnPService::UPnPService()
+ {
+ }
+
+ UPnPService::UPnPService(const UPnPService & s)
+ {
+ this->servicetype = s.servicetype;
+ this->controlurl = s.controlurl;
+ this->eventsuburl = s.eventsuburl;
+ this->serviceid = s.serviceid;
+ this->scpdurl = s.scpdurl;
+ }
+
+ void UPnPService::setProperty(const QString & name,const QString & value)
+ {
+ if (name == "serviceType")
+ servicetype = value;
+ else if (name == "controlURL")
+ controlurl = value;
+ else if (name == "eventSubURL")
+ eventsuburl = value;
+ else if (name == "SCPDURL")
+ scpdurl = value;
+ else if (name == "serviceId")
+ serviceid = value;
+ }
+
+ void UPnPService::clear()
+ {
+ servicetype = controlurl = eventsuburl = scpdurl = serviceid = "";
+ }
+
+ void UPnPService::debugPrintData()
+ {
+ Out(SYS_PNP|LOG_DEBUG) << " servicetype = " << servicetype << endl;
+ Out(SYS_PNP|LOG_DEBUG) << " controlurl = " << controlurl << endl;
+ Out(SYS_PNP|LOG_DEBUG) << " eventsuburl = " << eventsuburl << endl;
+ Out(SYS_PNP|LOG_DEBUG) << " scpdurl = " << scpdurl << endl;
+ Out(SYS_PNP|LOG_DEBUG) << " serviceid = " << serviceid << endl;
+ }
+
+ UPnPService & UPnPService::operator = (const UPnPService & s)
+ {
+ this->servicetype = s.servicetype;
+ this->controlurl = s.controlurl;
+ this->eventsuburl = s.eventsuburl;
+ this->serviceid = s.serviceid;
+ this->scpdurl = s.scpdurl;
+ return *this;
+ }
+
+ ///////////////////////////////////////
+
+ void UPnPDeviceDescription::setProperty(const QString & name,const QString & value)
+ {
+ if (name == "friendlyName")
+ friendlyName = value;
+ else if (name == "manufacturer")
+ manufacturer = value;
+ else if (name == "modelDescription")
+ modelDescription = value;
+ else if (name == "modelName")
+ modelName = value;
+ else if (name == "modelNumber")
+ modelNumber == value;
+ }
+
+ ///////////////////////////////////////
+
+ UPnPRouter::UPnPRouter(const QString & server,const KURL & location,bool verbose) : server(server),location(location),verbose(verbose)
+ {
+ // make the tmp_file unique, current time * a random number should be enough
+ tmp_file = QString("/tmp/ktorrent_upnp_description-%1.xml").arg(bt::GetCurrentTime() * rand());
+ }
+
+
+ UPnPRouter::~UPnPRouter()
+ {
+ QValueList<HTTPRequest*>::iterator i = active_reqs.begin();
+ while (i != active_reqs.end())
+ {
+ (*i)->deleteLater();
+ i++;
+ }
+ }
+
+ void UPnPRouter::addService(const UPnPService & s)
+ {
+ QValueList<UPnPService>::iterator i = services.begin();
+ while (i != services.end())
+ {
+ UPnPService & os = *i;
+ if (s.servicetype == os.servicetype)
+ return;
+ i++;
+ }
+ services.append(s);
+ }
+
+ void UPnPRouter::downloadFinished(KIO::Job* j)
+ {
+ if (j->error())
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "Failed to download " << location << " : " << j->errorString() << endl;
+ return;
+ }
+
+ QString target = tmp_file;
+ // load in the file (target is always local)
+ UPnPDescriptionParser desc_parse;
+ bool ret = desc_parse.parse(target,this);
+ if (!ret)
+ {
+ Out(SYS_PNP|LOG_IMPORTANT) << "Error parsing router description !" << endl;
+ QString dest = KGlobal::dirs()->saveLocation("data","ktorrent") + "upnp_failure";
+ KIO::file_copy(target,dest,-1,true,false,false);
+ }
+ else
+ {
+ if (verbose)
+ debugPrintData();
+ }
+ xmlFileDownloaded(this,ret);
+ bt::Delete(target);
+ }
+
+ void UPnPRouter::downloadXMLFile()
+ {
+ // downlaod XML description into a temporary file in /tmp
+ KIO::Job* job = KIO::file_copy(location,tmp_file,-1,true,false,false);
+ connect(job,SIGNAL(result(KIO::Job *)),this,SLOT(downloadFinished( KIO::Job* )));
+ }
+
+ void UPnPRouter::debugPrintData()
+ {
+ Out(SYS_PNP|LOG_DEBUG) << "UPnPRouter : " << endl;
+ Out(SYS_PNP|LOG_DEBUG) << "Friendly name = " << desc.friendlyName << endl;
+ Out(SYS_PNP|LOG_DEBUG) << "Manufacterer = " << desc.manufacturer << endl;
+ Out(SYS_PNP|LOG_DEBUG) << "Model description = " << desc.modelDescription << endl;
+ Out(SYS_PNP|LOG_DEBUG) << "Model name = " << desc.modelName << endl;
+ Out(SYS_PNP|LOG_DEBUG) << "Model number = " << desc.modelNumber << endl;
+ for (QValueList<UPnPService>::iterator i = services.begin();i != services.end();i++)
+ {
+ UPnPService & s = *i;
+ Out() << "Service : " << endl;
+ s.debugPrintData();
+ Out(SYS_PNP|LOG_DEBUG) << "Done" << endl;
+ }
+ Out(SYS_PNP|LOG_DEBUG) << "Done" << endl;
+ }
+
+
+ void UPnPRouter::forward(UPnPService* srv,const net::Port & port)
+ {
+ // add all the arguments for the command
+ QValueList<SOAP::Arg> args;
+ SOAP::Arg a;
+ a.element = "NewRemoteHost";
+ args.append(a);
+
+ // the external port
+ a.element = "NewExternalPort";
+ a.value = QString::number(port.number);
+ args.append(a);
+
+ // the protocol
+ a.element = "NewProtocol";
+ a.value = port.proto == TCP ? "TCP" : "UDP";
+ args.append(a);
+
+ // the local port
+ a.element = "NewInternalPort";
+ a.value = QString::number(port.number);
+ args.append(a);
+
+ // the local IP address
+ a.element = "NewInternalClient";
+ a.value = "$LOCAL_IP";// will be replaced by our local ip in bt::HTTPRequest
+ args.append(a);
+
+ a.element = "NewEnabled";
+ a.value = "1";
+ args.append(a);
+
+ a.element = "NewPortMappingDescription";
+ static Uint32 cnt = 0;
+ a.value = QString("KTorrent UPNP %1").arg(cnt++); // TODO: change this
+ args.append(a);
+
+ a.element = "NewLeaseDuration";
+ a.value = "0";
+ args.append(a);
+
+ QString action = "AddPortMapping";
+ QString comm = SOAP::createCommand(action,srv->servicetype,args);
+
+ Forwarding fw = {port,0,srv};
+ // erase old forwarding if one exists
+ QValueList<Forwarding>::iterator itr = fwds.begin();
+ while (itr != fwds.end())
+ {
+ Forwarding & fwo = *itr;
+ if (fwo.port == port && fwo.service == srv)
+ itr = fwds.erase(itr);
+ else
+ itr++;
+ }
+
+ fw.pending_req = sendSoapQuery(comm,srv->servicetype + "#" + action,srv->controlurl);
+ fwds.append(fw);
+ }
+
+ void UPnPRouter::forward(const net::Port & port)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Forwarding port " << port.number << " (" << (port.proto == UDP ? "UDP" : "TCP") << ")" << endl;
+ // first find the right service
+ QValueList<UPnPService>::iterator i = services.begin();
+ while (i != services.end())
+ {
+ UPnPService & s = *i;
+ if (s.servicetype == "urn:schemas-upnp-org:service:WANIPConnection:1" ||
+ s.servicetype == "urn:schemas-upnp-org:service:WANPPPConnection:1")
+ {
+ forward(&s,port);
+ }
+ i++;
+ }
+
+ }
+
+ void UPnPRouter::undoForward(UPnPService* srv,const net::Port & port,bt::WaitJob* waitjob)
+ {
+ // add all the arguments for the command
+ QValueList<SOAP::Arg> args;
+ SOAP::Arg a;
+ a.element = "NewRemoteHost";
+ args.append(a);
+
+ // the external port
+ a.element = "NewExternalPort";
+ a.value = QString::number(port.number);
+ args.append(a);
+
+ // the protocol
+ a.element = "NewProtocol";
+ a.value = port.proto == TCP ? "TCP" : "UDP";
+ args.append(a);
+
+
+ QString action = "DeletePortMapping";
+ QString comm = SOAP::createCommand(action,srv->servicetype,args);
+ bt::HTTPRequest* r = sendSoapQuery(comm,srv->servicetype + "#" + action,srv->controlurl,waitjob != 0);
+
+ if (waitjob)
+ waitjob->addExitOperation(r);
+
+ updateGUI();
+ }
+
+
+ void UPnPRouter::undoForward(const net::Port & port,bt::WaitJob* waitjob)
+ {
+ Out(SYS_PNP|LOG_NOTICE) << "Undoing forward of port " << port.number
+ << " (" << (port.proto == UDP ? "UDP" : "TCP") << ")" << endl;
+
+ QValueList<Forwarding>::iterator itr = fwds.begin();
+ while (itr != fwds.end())
+ {
+ Forwarding & wd = *itr;
+ if (wd.port == port)
+ {
+ undoForward(wd.service,wd.port,waitjob);
+ itr = fwds.erase(itr);
+ }
+ else
+ {
+ itr++;
+ }
+ }
+ }
+
+ bt::HTTPRequest* UPnPRouter::sendSoapQuery(const QString & query,const QString & soapact,const QString & controlurl,bool at_exit)
+ {
+ // if port is not set, 0 will be returned
+ // thanks to Diego R. Brogna for spotting this bug
+ if (location.port()==0)
+ location.setPort(80);
+
+ QString http_hdr = QString(
+ "POST %1 HTTP/1.1\r\n"
+ "HOST: %2:%3\r\n"
+ "Content-length: $CONTENT_LENGTH\r\n"
+ "Content-Type: text/xml\r\n"
+ "SOAPAction: \"%4\"\r\n"
+ "\r\n").arg(controlurl).arg(location.host()).arg(location.port()).arg(soapact);
+
+
+ HTTPRequest* r = new HTTPRequest(http_hdr,query,location.host(),location.port(),verbose);
+ connect(r,SIGNAL(replyError(bt::HTTPRequest* ,const QString& )),
+ this,SLOT(onReplyError(bt::HTTPRequest* ,const QString& )));
+ connect(r,SIGNAL(replyOK(bt::HTTPRequest* ,const QString& )),
+ this,SLOT(onReplyOK(bt::HTTPRequest* ,const QString& )));
+ connect(r,SIGNAL(error(bt::HTTPRequest*, bool )),
+ this,SLOT(onError(bt::HTTPRequest*, bool )));
+ r->start();
+ if (!at_exit)
+ active_reqs.append(r);
+ return r;
+ }
+
+ void UPnPRouter::httpRequestDone(bt::HTTPRequest* r,bool erase_fwd)
+ {
+ QValueList<Forwarding>::iterator i = fwds.begin();
+ while (i != fwds.end())
+ {
+ Forwarding & fw = *i;
+ if (fw.pending_req == r)
+ {
+ fw.pending_req = 0;
+ if (erase_fwd)
+ fwds.erase(i);
+ break;
+ }
+ i++;
+ }
+
+ updateGUI();
+ active_reqs.remove(r);
+ r->deleteLater();
+ }
+
+ void UPnPRouter::onReplyOK(bt::HTTPRequest* r,const QString &)
+ {
+ if (verbose)
+ Out(SYS_PNP|LOG_NOTICE) << "UPnPRouter : OK" << endl;
+
+ httpRequestDone(r,false);
+ }
+
+ void UPnPRouter::onReplyError(bt::HTTPRequest* r,const QString &)
+ {
+ if (verbose)
+ Out(SYS_PNP|LOG_IMPORTANT) << "UPnPRouter : Error" << endl;
+
+ httpRequestDone(r,true);
+
+ }
+
+ void UPnPRouter::onError(bt::HTTPRequest* r,bool)
+ {
+ httpRequestDone(r,true);
+ }
+
+#if 0
+ QValueList<UPnPService>::iterator UPnPRouter::findPortForwardingService()
+ {
+ QValueList<UPnPService>::iterator i = services.begin();
+ while (i != services.end())
+ {
+ UPnPService & s = *i;
+ if (s.servicetype == "urn:schemas-upnp-org:service:WANIPConnection:1" ||
+ s.servicetype == "urn:schemas-upnp-org:service:WANPPPConnection:1")
+ return i;
+ i++;
+ }
+ return services.end();
+ }
+
+
+ void UPnPRouter::getExternalIP()
+ {
+ // first find the right service
+ QValueList<UPnPService>::iterator i = findPortForwardingService();
+ if (i == services.end())
+ throw Error(i18n("Cannot find port forwarding service in the device's description!"));
+
+ UPnPService & s = *i;
+ QString action = "GetExternalIPAddress";
+ QString comm = SOAP::createCommand(action,s.servicetype);
+ sendSoapQuery(comm,s.servicetype + "#" + action,s.controlurl);
+ }
+
+ void UPnPRouter::isPortForwarded(const net::Port & port)
+ {
+ // first find the right service
+ QValueList<UPnPService>::iterator i = findPortForwardingService();
+ if (i == services.end())
+ throw Error(i18n("Cannot find port forwarding service in the device's description!"));
+
+ // add all the arguments for the command
+ QValueList<SOAP::Arg> args;
+ SOAP::Arg a;
+ a.element = "NewRemoteHost";
+ args.append(a);
+
+ // the external port
+ a.element = "NewExternalPort";
+ a.value = QString::number(port.number);
+ args.append(a);
+
+ // the protocol
+ a.element = "NewProtocol";
+ a.value = port.proto == TCP ? "TCP" : "UDP";
+ args.append(a);
+
+ UPnPService & s = *i;
+ QString action = "GetSpecificPortMappingEntry";
+ QString comm = SOAP::createCommand(action,s.servicetype,args);
+ sendSoapQuery(comm,s.servicetype + "#" + action,s.controlurl);
+ }
+#endif
+
+
+}
+
+#include "upnprouter.moc"
diff --git a/plugins/upnp/upnprouter.h b/plugins/upnp/upnprouter.h
new file mode 100644
index 0000000..a4d32b4
--- /dev/null
+++ b/plugins/upnp/upnprouter.h
@@ -0,0 +1,223 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * joris.guisson@gmail.com *
+ * *
+ * 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 option) 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., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#ifndef KTUPNPROUTER_H
+#define KTUPNPROUTER_H
+
+#include <kurl.h>
+#include <qstringlist.h>
+#include <kstreamsocket.h>
+#include <net/portlist.h>
+
+using bt::Uint16;
+
+namespace bt
+{
+ class HTTPRequest;
+ class WaitJob;
+}
+
+namespace KIO
+{
+ class Job;
+}
+
+namespace kt
+{
+ /**
+ * Structure describing a UPnP service found in an xml file.
+ */
+ struct UPnPService
+ {
+ QString serviceid;
+ QString servicetype;
+ QString controlurl;
+ QString eventsuburl;
+ QString scpdurl;
+
+ UPnPService();
+ UPnPService(const UPnPService & s);
+
+ /**
+ * Set a property of the service.
+ * @param name Name of the property (matches to variable names)
+ * @param value Value of the property
+ */
+ void setProperty(const QString & name,const QString & value);
+
+ /**
+ * Set all strings to empty.
+ */
+ void clear();
+
+ /// Print the data of this service
+ void debugPrintData();
+
+ /**
+ * Assignment operator
+ * @param s The service to copy
+ * @return *this
+ */
+ UPnPService & operator = (const UPnPService & s);
+ };
+
+ /**
+ * Struct to hold the description of a device
+ */
+ struct UPnPDeviceDescription
+ {
+ QString friendlyName;
+ QString manufacturer;
+ QString modelDescription;
+ QString modelName;
+ QString modelNumber;
+
+ /**
+ * Set a property of the description
+ * @param name Name of the property (matches to variable names)
+ * @param value Value of the property
+ */
+ void setProperty(const QString & name,const QString & value);
+ };
+
+ /**
+ * @author Joris Guisson
+ *
+ * Class representing a UPnP enabled router. This class is also used to communicate
+ * with the router.
+ */
+ class UPnPRouter : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ struct Forwarding
+ {
+ net::Port port;
+ bt::HTTPRequest* pending_req;
+ UPnPService* service;
+ };
+ private:
+ QString server;
+ QString tmp_file;
+ KURL location;
+ UPnPDeviceDescription desc;
+ QValueList<UPnPService> services;
+ QValueList<Forwarding> fwds;
+ QValueList<bt::HTTPRequest*> active_reqs;
+ public:
+ /**
+ * Construct a router.
+ * @param server The name of the router
+ * @param location The location of it's xml description file
+ * @param verbose Print lots of debug info
+ */
+ UPnPRouter(const QString & server,const KURL & location,bool verbose = false);
+ virtual ~UPnPRouter();
+
+ /// Get the name of the server
+ QString getServer() const {return server;}
+
+ /// Get the location of it's xml description
+ KURL getLocation() const {return location;}
+
+ /// Get the device description
+ UPnPDeviceDescription & getDescription() {return desc;}
+
+ /// Get the device description (const version)
+ const UPnPDeviceDescription & getDescription() const {return desc;}
+
+ /**
+ * Download the XML File of the router.
+ */
+ void downloadXMLFile();
+
+ /**
+ * Add a service to the router.
+ * @param s The service
+ */
+ void addService(const UPnPService & s);
+
+#if 0
+ /**
+ * See if a port is forwarded
+ * @param port The Port
+ */
+ void isPortForwarded(const net::Port & port);
+
+ /**
+ * Get the external IP address.
+ */
+ void getExternalIP();
+#endif
+
+ /**
+ * Forward a local port
+ * @param port The local port to forward
+ */
+ void forward(const net::Port & port);
+
+ /**
+ * Undo forwarding
+ * @param port The port
+ * @param waitjob When this is set the jobs needs to be added to the waitjob,
+ * so we can wait for their completeion at exit
+ */
+ void undoForward(const net::Port & port,bt::WaitJob* waitjob = 0);
+
+ void debugPrintData();
+
+ QValueList<Forwarding>::iterator beginPortMappings() {return fwds.begin();}
+ QValueList<Forwarding>::iterator endPortMappings() {return fwds.end();}
+
+ private slots:
+ void onReplyOK(bt::HTTPRequest* r,const QString &);
+ void onReplyError(bt::HTTPRequest* r,const QString &);
+ void onError(bt::HTTPRequest* r,bool);
+ void downloadFinished(KIO::Job* j);
+
+
+
+ signals:
+ /**
+ * Tell the GUI that it needs to be updated.
+ */
+ void updateGUI();
+
+ /**
+ * Signal which indicates that the XML was downloaded successfully or not.
+ * @param r The router which emitted the signal
+ * @param success Wether or not it succeeded
+ */
+ void xmlFileDownloaded(UPnPRouter* r,bool success);
+
+ private:
+ QValueList<UPnPService>::iterator findPortForwardingService();
+
+ bt::HTTPRequest* sendSoapQuery(const QString & query,const QString & soapact,const QString & controlurl,bool at_exit = false);
+ bool verbose;
+
+ void forward(UPnPService* srv,const net::Port & port);
+ void undoForward(UPnPService* srv,const net::Port & port,bt::WaitJob* waitjob);
+ void httpRequestDone(bt::HTTPRequest* r,bool erase_fwd);
+ };
+
+}
+
+#endif
diff --git a/plugins/upnp/upnpwidget.ui b/plugins/upnp/upnpwidget.ui
new file mode 100644
index 0000000..a8f0f7a
--- /dev/null
+++ b/plugins/upnp/upnpwidget.ui
@@ -0,0 +1,139 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>UPnPWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>UPnPWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>561</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>UPnP</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Detected devices:</string>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Device</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Ports Forwarded</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>WAN Connection</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_device_list</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_forward_btn</cstring>
+ </property>
+ <property name="text">
+ <string>Forw&amp;ard Ports</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_undo_forward_btn</cstring>
+ </property>
+ <property name="text">
+ <string>Undo Port Forwarding</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>70</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_rescan</cstring>
+ </property>
+ <property name="text">
+ <string>Rescan</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>