summaryrefslogtreecommitdiffstats
path: root/src/upnp
diff options
context:
space:
mode:
Diffstat (limited to 'src/upnp')
-rw-r--r--src/upnp/Makefile.am14
-rw-r--r--src/upnp/exitoperation.cpp47
-rw-r--r--src/upnp/exitoperation.h67
-rw-r--r--src/upnp/forwardportlist.cpp83
-rw-r--r--src/upnp/forwardportlist.h101
-rw-r--r--src/upnp/httprequest.cpp123
-rw-r--r--src/upnp/httprequest.h98
-rw-r--r--src/upnp/portlist.cpp73
-rw-r--r--src/upnp/portlist.h103
-rw-r--r--src/upnp/soap.cpp53
-rw-r--r--src/upnp/soap.h62
-rw-r--r--src/upnp/upnpdescriptionparser.cpp220
-rw-r--r--src/upnp/upnpdescriptionparser.h49
-rw-r--r--src/upnp/upnpmcastsocket.cpp313
-rw-r--r--src/upnp/upnpmcastsocket.h91
-rw-r--r--src/upnp/upnprouter.cpp531
-rw-r--r--src/upnp/upnprouter.h303
17 files changed, 2331 insertions, 0 deletions
diff --git a/src/upnp/Makefile.am b/src/upnp/Makefile.am
new file mode 100644
index 0000000..97a2708
--- /dev/null
+++ b/src/upnp/Makefile.am
@@ -0,0 +1,14 @@
+METASOURCES = AUTO
+INCLUDES = $(all_includes)
+
+libktupnp_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libktupnp.la
+libktupnp_la_SOURCES = soap.cpp upnpdescriptionparser.cpp upnpmcastsocket.cpp \
+ upnprouter.cpp portlist.cpp httprequest.cpp exitoperation.cpp \
+ forwardportlist.cpp
+
+noinst_HEADERS = upnpmcastsocket.h upnprouter.h \
+ upnpdescriptionparser.h soap.h
+
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS) $(USE_RTTI)
diff --git a/src/upnp/exitoperation.cpp b/src/upnp/exitoperation.cpp
new file mode 100644
index 0000000..8eedb7a
--- /dev/null
+++ b/src/upnp/exitoperation.cpp
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * 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 "exitoperation.h"
+
+namespace kt
+{
+
+ ExitOperation::ExitOperation()
+ {}
+
+
+ ExitOperation::~ExitOperation()
+ {}
+
+ ExitJobOperation::ExitJobOperation(KIO::Job* j)
+ {
+ connect(j,SIGNAL(result(KIO::Job*)),this,SLOT(onResult( KIO::Job* )));
+ }
+
+ ExitJobOperation::~ExitJobOperation()
+ {
+ }
+
+ void ExitJobOperation::onResult(KIO::Job* )
+ {
+ operationFinished(this);
+ }
+
+}
+#include "exitoperation.moc"
diff --git a/src/upnp/exitoperation.h b/src/upnp/exitoperation.h
new file mode 100644
index 0000000..edaa2fa
--- /dev/null
+++ b/src/upnp/exitoperation.h
@@ -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. *
+ ***************************************************************************/
+#ifndef KTEXITOPERATION_H
+#define KTEXITOPERATION_H
+
+#include <qobject.h>
+#include <kio/job.h>
+
+namespace kt
+{
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Object to derive from for operations which need to be performed at exit.
+ * The operation should emit the operationFinished signal when they are done.
+ *
+ * ExitOperation's can be used in combination with a WaitJob, to wait for a certain amount of time
+ * to give serveral ExitOperation's the time time to finish up.
+ */
+ class ExitOperation : public QObject
+ {
+ Q_OBJECT
+ public:
+ ExitOperation();
+ virtual ~ExitOperation();
+
+ /// wether or not we can do a deleteLater on the job after it has finished.
+ virtual bool deleteAllowed() const {return true;}
+ signals:
+ void operationFinished(kt::ExitOperation* opt);
+ };
+
+ /**
+ * Exit operation which waits for a KIO::Job
+ */
+ class ExitJobOperation : public ExitOperation
+ {
+ Q_OBJECT
+ public:
+ ExitJobOperation(KIO::Job* j);
+ virtual ~ExitJobOperation();
+
+ virtual bool deleteAllowed() const {return false;}
+ private slots:
+ virtual void onResult(KIO::Job* j);
+ };
+}
+
+#endif
diff --git a/src/upnp/forwardportlist.cpp b/src/upnp/forwardportlist.cpp
new file mode 100644
index 0000000..906bde3
--- /dev/null
+++ b/src/upnp/forwardportlist.cpp
@@ -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. *
+ ***************************************************************************/
+#include "forwardportlist.h"
+#include <kdebug.h>
+namespace net
+{
+ ForwardPort::ForwardPort() : extnumber(0),intnumber(0),proto(TCP),forward(false)
+ {
+ }
+
+ ForwardPort::ForwardPort(bt::Uint16 extnumber,bt::Uint16 intnumber,Protocol proto,bool forward)
+ : extnumber(extnumber),intnumber(intnumber),proto(proto),forward(forward)
+ {
+ }
+
+ ForwardPort::ForwardPort(const ForwardPort & p) : extnumber(p.extnumber),
+ intnumber(p.intnumber),proto(p.proto),forward(p.forward)
+ {
+ }
+
+
+ bool ForwardPort::operator == (const ForwardPort & p) const
+ {
+ return extnumber == p.extnumber && intnumber == p.intnumber && proto == p.proto;
+ }
+
+ ForwardPortList::ForwardPortList() : lst(0)
+ {}
+
+
+ ForwardPortList::~ForwardPortList()
+ {}
+
+
+ void ForwardPortList::addNewForwardPort(bt::Uint16 extnumber,bt::Uint16 intnumber,Protocol proto,
+ bool forward)
+ {
+ kdDebug() << "adding forward port" << endl;
+
+ ForwardPort p = ForwardPort(extnumber,intnumber,proto,forward);
+ append(p);
+ if (lst)
+ lst->portAdded(p);
+ kdDebug() << "added forward port" << endl;
+
+ }
+
+
+ void ForwardPortList::removeForwardPort(bt::Uint16 extnumber,bt::Uint16 intnumber,Protocol proto)
+ {
+ kdDebug() << "removing forward port" << endl;
+ ForwardPortList::iterator itr = find(ForwardPort(extnumber,intnumber,proto,false));
+ if (itr == end())
+ return;
+
+ if (lst)
+ lst->portRemoved(*itr);
+
+ erase(itr);
+ kdDebug() << "removed forward port" << endl;
+
+ }
+
+
+
+}
diff --git a/src/upnp/forwardportlist.h b/src/upnp/forwardportlist.h
new file mode 100644
index 0000000..2a27f59
--- /dev/null
+++ b/src/upnp/forwardportlist.h
@@ -0,0 +1,101 @@
+/***************************************************************************
+ * 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 FORWARDPORTLIST_H
+#define FORWARDPORTLIST_H
+
+#include <qvaluelist.h>
+#include "../constants.h"
+#include "portlist.h"
+
+namespace net
+{
+
+
+ struct ForwardPort
+ {
+ bt::Uint16 extnumber;
+ bt::Uint16 intnumber;
+ Protocol proto;
+ bool forward;
+
+ ForwardPort();
+ ForwardPort(bt::Uint16 extnumber,bt::Uint16 intnumber,Protocol proto,bool forward);
+ ForwardPort(const ForwardPort & p);
+
+ bool operator == (const ForwardPort & p) const;
+ };
+
+ /**
+ * Listener class for the ForwardPortList.
+ */
+ class ForwardPortListener
+ {
+ public:
+ /**
+ * A port has been added.
+ * @param port The port
+ */
+ virtual void portAdded(const ForwardPort & port) = 0;
+
+ /**
+ * A port has been removed
+ * @param port The port
+ */
+ virtual void portRemoved(const ForwardPort & port) = 0;
+ };
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * List of ports which are currently being used.
+ *
+ */
+ class ForwardPortList : public QValueList<ForwardPort>
+ {
+ ForwardPortListener* lst;
+ public:
+ ForwardPortList();
+ virtual ~ForwardPortList();
+
+ /**
+ * When a port is in use, this function needs to be called.
+ * @param number ForwardPort number
+ * @param proto Protocol
+ * @param forward Wether or not it needs to be forwarded
+ */
+ void addNewForwardPort(bt::Uint16 extnumber,bt::Uint16 intnumber,Protocol proto,bool forward);
+
+ /**
+ * Needs to be called when a port is not being using anymore.
+ * @param number ForwardPort number
+ * @param proto Protocol
+ */
+ void removeForwardPort(bt::Uint16 extnumber,bt::Uint16 intnumber,Protocol proto);
+
+ /**
+ * Set the port listener.
+ * @param pl ForwardPort listener
+ */
+ void setListener(ForwardPortListener* pl) {lst = pl;}
+ };
+
+}
+
+#endif
diff --git a/src/upnp/httprequest.cpp b/src/upnp/httprequest.cpp
new file mode 100644
index 0000000..5a612b0
--- /dev/null
+++ b/src/upnp/httprequest.cpp
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * 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 <qstringlist.h>
+#include "httprequest.h"
+#include "../functions.h"
+#include <kdebug.h>
+#include <ksocks.h>
+
+
+namespace bt
+{
+
+ HTTPRequest::HTTPRequest(const QString & hdr,const QString & payload,const QString & host,Uint16 port,bool verbose, bool fwd) : hdr(hdr),payload(payload),verbose(verbose),fwd(fwd)
+ {
+ KSocks::self()->disableSocks();
+ sock = new KNetwork::KStreamSocket(host,QString::number(port),this,0);
+ sock->enableRead(true);
+ sock->enableWrite(true);
+ sock->setTimeout(30000);
+ sock->setBlocking(false);
+ connect(sock,SIGNAL(readyRead()),this,SLOT(onReadyRead()));
+ connect(sock,SIGNAL(gotError(int)),this,SLOT(onError(int )));
+ connect(sock,SIGNAL(timedOut()),this,SLOT(onTimeout()));
+ connect(sock,SIGNAL(connected(const KResolverEntry&)),
+ this, SLOT(onConnect( const KResolverEntry& )));
+ }
+
+
+ HTTPRequest::~HTTPRequest()
+ {
+ sock->close();
+ delete sock;
+ }
+
+ void HTTPRequest::start()
+ {
+ sock->connect();
+ }
+
+ void HTTPRequest::onConnect(const KResolverEntry&)
+ {
+ payload = payload.replace("$LOCAL_IP",sock->localAddress().nodeName());
+ hdr = hdr.replace("$CONTENT_LENGTH",QString::number(payload.length()));
+
+ QString req = hdr + payload;
+/* if (verbose)
+ {
+ KdDebug() << "Sending " << endl;
+ KdDebug() << hdr << payload << endl;
+ }*/
+ sock->writeBlock(req.ascii(),req.length());
+ }
+
+ void HTTPRequest::onReadyRead()
+ {
+ Uint32 ba = sock->bytesAvailable();
+ if (ba == 0)
+ {
+ error(this,false);
+ sock->close();
+ return;
+ }
+
+ Array<char> data(ba);
+ ba = sock->readBlock(data,ba);
+ QString strdata((const char*)data);
+ QStringList sl = QStringList::split("\r\n",strdata,false);
+
+/* if (verbose)
+ {
+ KdDebug() << "Got reply : " << endl;
+ KdDebug() << strdata << endl;
+ }*/
+
+ if (sl.first().contains("HTTP") && sl.first().contains("200"))
+ {
+ // emit reply OK
+ replyOK(this,sl.last(),fwd);
+ }
+ else
+ {
+ // emit reply error
+ replyError(this,sl.last(),fwd);
+ }
+ operationFinished(this);
+ }
+
+ void HTTPRequest::onError(int)
+ {
+ kdDebug() << "HTTPRequest error : " << sock->errorString() << endl;
+ error(this,false);
+ sock->close();
+ operationFinished(this);
+ }
+
+ void HTTPRequest::onTimeout()
+ {
+ kdDebug() << "HTTPRequest timeout" << endl;
+ error(this,true);
+ sock->close();
+ operationFinished(this);
+ }
+
+
+}
+#include "httprequest.moc"
diff --git a/src/upnp/httprequest.h b/src/upnp/httprequest.h
new file mode 100644
index 0000000..9832da8
--- /dev/null
+++ b/src/upnp/httprequest.h
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * 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 BTHTTPREQUEST_H
+#define BTHTTPREQUEST_H
+
+#include <qobject.h>
+#include <kurl.h>
+#include <kstreamsocket.h>
+#include "exitoperation.h"
+#include "../constants.h"
+
+using KNetwork::KResolverEntry;
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson
+ *
+ * Just create one, fill in the fields,
+ * connect to the right signals and forget about it. After the reply has been received or
+ * an error occurred, the appropriate signal will be emitted.
+ */
+ class HTTPRequest : public kt::ExitOperation
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Constructor, set the url and the request header.
+ * @param hdr The http request header
+ * @param payload The payload
+ * @param host The host
+ * @param port THe port
+ * @param verbose Print traffic to the log
+ */
+ HTTPRequest(const QString & hdr,const QString & payload,const QString & host,
+ Uint16 port,bool verbose, bool fwd);
+ virtual ~HTTPRequest();
+
+ /**
+ * Open a connetion and send the request.
+ */
+ void start();
+ QString showPayload(){return payload;};
+ signals:
+ /**
+ * An OK reply was sent.
+ * @param r The sender of the request
+ * @param data The data of the reply
+ */
+ void replyOK(bt::HTTPRequest* r,const QString & data, bool fwd);
+
+ /**
+ * Anything else but an 200 OK was sent.
+ * @param r The sender of the request
+ * @param data The data of the reply
+ */
+ void replyError(bt::HTTPRequest* r,const QString & data, bool fwd);
+
+ /**
+ * No reply was sent and an error or timeout occurred.
+ * @param r The sender of the request
+ * @param timeout Wether or not a timeout occurred
+ */
+ void error(bt::HTTPRequest* r,bool timeout);
+
+ private slots:
+ void onReadyRead();
+ void onError(int);
+ void onTimeout();
+ void onConnect(const KResolverEntry&);
+
+ private:
+ KNetwork::KStreamSocket* sock;
+ QString hdr,payload;
+ bool verbose,fwd;
+ };
+
+}
+
+#endif
diff --git a/src/upnp/portlist.cpp b/src/upnp/portlist.cpp
new file mode 100644
index 0000000..56076ed
--- /dev/null
+++ b/src/upnp/portlist.cpp
@@ -0,0 +1,73 @@
+/***************************************************************************
+ * 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 "portlist.h"
+
+namespace net
+{
+ Port::Port() : number(0),proto(TCP),forward(false)
+ {
+ }
+
+ Port::Port(bt::Uint16 number,Protocol proto,bool forward)
+ : number(number),proto(proto),forward(forward)
+ {
+ }
+
+ Port::Port(const Port & p) : number(p.number),proto(p.proto),forward(p.forward)
+ {
+ }
+
+ bool Port::operator == (const Port & p) const
+ {
+ return number == p.number && proto == p.proto;
+ }
+
+ PortList::PortList() : lst(0)
+ {}
+
+
+ PortList::~PortList()
+ {}
+
+
+ void PortList::addNewPort(bt::Uint16 number,Protocol proto,bool forward)
+ {
+ Port p = Port(number,proto,forward);
+ append(p);
+ if (lst)
+ lst->portAdded(p);
+ }
+
+
+ void PortList::removePort(bt::Uint16 number,Protocol proto)
+ {
+ PortList::iterator itr = find(Port(number,proto,false));
+ if (itr == end())
+ return;
+
+ if (lst)
+ lst->portRemoved(*itr);
+
+ erase(itr);
+ }
+
+
+
+}
diff --git a/src/upnp/portlist.h b/src/upnp/portlist.h
new file mode 100644
index 0000000..5063b78
--- /dev/null
+++ b/src/upnp/portlist.h
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * 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 NETPORTLIST_H
+#define NETPORTLIST_H
+
+#include <qvaluelist.h>
+#include "../constants.h"
+
+namespace net
+{
+ enum Protocol
+ {
+ TCP,
+ UDP
+ };
+
+ struct Port
+ {
+ bt::Uint16 number;
+ Protocol proto;
+ bool forward;
+
+ Port();
+ Port(bt::Uint16 number,Protocol proto,bool forward);
+ Port(const Port & p);
+
+ bool operator == (const Port & p) const;
+ };
+
+ /**
+ * Listener class for the PortList.
+ */
+ class PortListener
+ {
+ public:
+ /**
+ * A port has been added.
+ * @param port The port
+ */
+ virtual void portAdded(const Port & port) = 0;
+
+ /**
+ * A port has been removed
+ * @param port The port
+ */
+ virtual void portRemoved(const Port & port) = 0;
+ };
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * List of ports which are currently being used.
+ *
+ */
+ class PortList : public QValueList<Port>
+ {
+ PortListener* lst;
+ public:
+ PortList();
+ virtual ~PortList();
+
+ /**
+ * When a port is in use, this function needs to be called.
+ * @param number Port number
+ * @param proto Protocol
+ * @param forward Wether or not it needs to be forwarded
+ */
+ void addNewPort(bt::Uint16 number,Protocol proto,bool forward);
+
+ /**
+ * Needs to be called when a port is not being using anymore.
+ * @param number Port number
+ * @param proto Protocol
+ */
+ void removePort(bt::Uint16 number,Protocol proto);
+
+ /**
+ * Set the port listener.
+ * @param pl Port listener
+ */
+ void setListener(PortListener* pl) {lst = pl;}
+ };
+
+}
+
+#endif
diff --git a/src/upnp/soap.cpp b/src/upnp/soap.cpp
new file mode 100644
index 0000000..b155b55
--- /dev/null
+++ b/src/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/src/upnp/soap.h b/src/upnp/soap.h
new file mode 100644
index 0000000..c11e2ed
--- /dev/null
+++ b/src/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/src/upnp/upnpdescriptionparser.cpp b/src/upnp/upnpdescriptionparser.cpp
new file mode 100644
index 0000000..f27b6cd
--- /dev/null
+++ b/src/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 <kdebug.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)
+ {
+ kdDebug() << "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/src/upnp/upnpdescriptionparser.h b/src/upnp/upnpdescriptionparser.h
new file mode 100644
index 0000000..5d4bf1e
--- /dev/null
+++ b/src/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/src/upnp/upnpmcastsocket.cpp b/src/upnp/upnpmcastsocket.cpp
new file mode 100644
index 0000000..48159c2
--- /dev/null
+++ b/src/upnp/upnpmcastsocket.cpp
@@ -0,0 +1,313 @@
+/***************************************************************************
+ * 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 <kdebug.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)))
+ kdDebug() << "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()
+ {
+ kdDebug() << "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)
+ {
+ kdDebug() << "Sending : " << endl;
+ kdDebug() << 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)
+ {
+ kdDebug() << "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)
+ {
+ kdDebug() << "Received : " << endl;
+ kdDebug() << 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;
+
+
+ kdDebug() << "Received : " << endl;
+ for (Uint32 idx = 0;idx < lines.count(); idx++)
+ kdDebug() << 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)
+ {
+ kdDebug() << "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
+ {
+ kdDebug() << "Detected IGD " << server << endl;
+ // everything OK, make a new UPnPRouter
+ return new UPnPRouter(server,location,verbose);
+ }
+ }
+
+ void UPnPMCastSocket::onError(int)
+ {
+ kdDebug() << "UPnPMCastSocket Error : " << errorString() << endl;
+ }
+
+ void UPnPMCastSocket::saveRouters(const QString & file)
+ {
+ QFile fptr(file);
+ if (!fptr.open(IO_WriteOnly))
+ {
+ kdDebug() << "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))
+ {
+ kdDebug() << "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)
+ {
+ kdDebug() << "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)
+ {
+ kdDebug() << "Failed to leave multicast group 239.255.255.250" << endl;
+ }
+ }
+}
+
+
+
+#include "upnpmcastsocket.moc"
diff --git a/src/upnp/upnpmcastsocket.h b/src/upnp/upnpmcastsocket.h
new file mode 100644
index 0000000..be75f4b
--- /dev/null
+++ b/src/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 <kdatagramsocket.h>
+#include "../constants.h"
+#include "../functions.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(kt::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/src/upnp/upnprouter.cpp b/src/upnp/upnprouter.cpp
new file mode 100644
index 0000000..4d8f5ad
--- /dev/null
+++ b/src/upnp/upnprouter.cpp
@@ -0,0 +1,531 @@
+/***************************************************************************
+ * 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 <kdebug.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <qstringlist.h>
+#include <kio/netaccess.h>
+#include <kio/job.h>
+#include "httprequest.h"
+#include "upnprouter.h"
+#include "upnpdescriptionparser.h"
+#include "soap.h"
+#include "../functions.h"
+#include <fcntl.h>
+#include <qdir.h>
+#include <qfile.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()
+ {
+ kdDebug() << " servicetype = " << servicetype << endl;
+ kdDebug() << " controlurl = " << controlurl << endl;
+ kdDebug() << " eventsuburl = " << eventsuburl << endl;
+ kdDebug() << " scpdurl = " << scpdurl << endl;
+ kdDebug() << " 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)
+ {
+ forwardedPortList = new ForwardPortList();
+ // make the tmp_file unique, current time * a random number should be enough
+ tmp_file = QString("/tmp/tork_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())
+ {
+ kdDebug() << "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)
+ {
+ kdDebug() << "Error parsing router description !" << endl;
+ QString dest = KGlobal::dirs()->saveLocation("data","tork") + "upnp_failure";
+ KIO::file_copy(target,dest,-1,true,false,false);
+ }
+ else
+ {
+ if (verbose)
+ debugPrintData();
+ }
+ xmlFileDownloaded(this,ret);
+ remove(QFile::encodeName(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()
+ {
+ kdDebug() << "UPnPRouter : " << endl;
+ kdDebug() << "Friendly name = " << desc.friendlyName << endl;
+ kdDebug() << "Manufacterer = " << desc.manufacturer << endl;
+ kdDebug() << "Model description = " << desc.modelDescription << endl;
+ kdDebug() << "Model name = " << desc.modelName << endl;
+ kdDebug() << "Model number = " << desc.modelNumber << endl;
+ for (QValueList<UPnPService>::iterator i = services.begin();i != services.end();i++)
+ {
+ UPnPService & s = *i;
+ kdDebug() << "Service : " << endl;
+ s.debugPrintData();
+ kdDebug() << "Done" << endl;
+ }
+ kdDebug() << "Done" << endl;
+ }
+
+
+ void UPnPRouter::forward(UPnPService* srv,const net::Port & externalport,
+ const net::Port & internalport)
+ {
+
+
+ // 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(externalport.number);
+ args.append(a);
+
+ // the protocol
+ a.element = "NewProtocol";
+ a.value = externalport.proto == TCP ? "TCP" : "UDP";
+ args.append(a);
+
+ // the local port
+ a.element = "NewInternalPort";
+ if (internalport.number)
+ a.value = QString::number(internalport.number);
+ else
+ a.value = QString::number(externalport.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("TorK 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 = {externalport,internalport,0,srv};
+ // erase old forwarding if one exists
+ QValueList<Forwarding>::iterator itr = fwds.begin();
+ while (itr != fwds.end())
+ {
+ Forwarding & fwo = *itr;
+ if (fwo.extport == externalport && fwo.intport == internalport && fwo.service == srv)
+ itr = fwds.erase(itr);
+ else
+ itr++;
+ }
+
+ fw.pending_req = sendSoapQuery(comm,srv->servicetype + "#" + action,srv->controlurl, true);
+ fwds.append(fw);
+
+ //Track the forwarding request so we can know whether it was successful or not
+ //and keep a map of successfully forwarded ports in forwardedPorts
+ ForwardingRequest fwreq = {externalport,internalport,fw.pending_req};
+ QValueList<ForwardingRequest>::iterator itrq = fwdreqs.begin();
+ while (itrq != fwdreqs.end())
+ {
+ ForwardingRequest & fwo = *itrq;
+ if (fwo.extport == externalport && fwo.intport == internalport)
+ itrq = fwdreqs.erase(itrq);
+ else
+ itrq++;
+ }
+ fwdreqs.append(fwreq);
+
+ }
+
+ void UPnPRouter::forward(const net::Port & externalport,
+ const net::Port & internalport,
+ bool force)
+ {
+
+ if ((forwardedPortList->contains(net::ForwardPort(externalport.number,
+ internalport.number,
+ net::TCP,false)))
+ && (!force))
+ return;
+
+ kdDebug() << "Forwarding port " << externalport.number << " (" << (externalport.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")
+ {
+ if (internalport.number)
+ forward(&s,externalport,internalport);
+ else
+ forward(&s,externalport);
+ }
+ i++;
+ }
+
+ }
+
+ void UPnPRouter::undoForward(UPnPService* srv,const net::Port & extport,
+ const net::Port & intport,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(extport.number);
+ args.append(a);
+
+ // the protocol
+ a.element = "NewProtocol";
+ a.value = extport.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,false);
+
+ ForwardingRequest fwreq = {extport,intport,r};
+ QValueList<ForwardingRequest>::iterator itrq = fwdreqs.begin();
+ while (itrq != fwdreqs.end())
+ {
+ ForwardingRequest & fwo = *itrq;
+ if (fwo.extport == extport && fwo.intport == intport)
+ itrq = fwdreqs.erase(itrq);
+ else
+ itrq++;
+ }
+ fwdreqs.append(fwreq);
+
+
+ if (waitjob)
+ waitjob->addExitOperation(r);
+
+ updateGUI();
+ }
+
+
+ void UPnPRouter::undoForward(const net::Port & externalport,
+ const net::Port & ,bt::WaitJob* waitjob)
+ {
+ kdDebug() << "Undoing forward of port " << externalport.number
+ << " (" << (externalport.proto == UDP ? "UDP" : "TCP") << ")" << endl;
+
+ QValueList<Forwarding>::iterator itr = fwds.begin();
+ while (itr != fwds.end())
+ {
+ Forwarding & wd = *itr;
+ if (wd.extport == externalport)
+ {
+ undoForward(wd.service,wd.extport,wd.intport,waitjob);
+ itr = fwds.erase(itr);
+ }
+ else
+ {
+ itr++;
+ }
+ }
+ }
+
+ bt::HTTPRequest* UPnPRouter::sendSoapQuery(const QString & query,const QString & soapact,const QString & controlurl, bool fwd,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, fwd);
+ connect(r,SIGNAL(replyError(bt::HTTPRequest* ,const QString& ,bool)),
+ this,SLOT(onReplyError(bt::HTTPRequest* ,const QString& ,bool)));
+ connect(r,SIGNAL(replyOK(bt::HTTPRequest* ,const QString& ,bool)),
+ this,SLOT(onReplyOK(bt::HTTPRequest* ,const QString& ,bool)));
+ 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 & s,bool fwd)
+ {
+ kdDebug() << "UPnPRouter : OK" << endl;
+ kdDebug() << "FWD : " << fwd << endl;
+ QValueList<ForwardingRequest>::iterator i = fwdreqs.begin();
+ while (i != fwdreqs.end())
+ {
+ ForwardingRequest & fw = *i;
+ if (fw.pending_req == r)
+ {
+ if (fwd)
+ forwardedPortList->addNewForwardPort(fw.extport.number,
+ fw.intport.number,net::TCP,false);
+ else
+ forwardedPortList->removeForwardPort(fw.extport.number,
+ fw.intport.number,net::TCP);
+ break;
+ }
+ i++;
+ }
+
+ emit replyOK(this,r,s,fwd);
+ httpRequestDone(r,false);
+ }
+
+ void UPnPRouter::onReplyError(bt::HTTPRequest* r,const QString & s,bool fwd)
+ {
+ if (verbose)
+ kdDebug() << "UPnPRouter : Error" << endl;
+ kdDebug() << r->showPayload() << endl;
+ httpRequestDone(r,true);
+ emit replyError(this,r,s,fwd);
+ }
+
+ void UPnPRouter::onError(bt::HTTPRequest* r,bool)
+ {
+ httpRequestDone(r,true);
+ }
+
+
+}
+
+
+namespace bt
+{
+
+ WaitJob::WaitJob(Uint32 millis) : KIO::Job(false)
+ {
+ connect(&timer,SIGNAL(timeout()),this,SLOT(timerDone()));
+ timer.start(millis,true);
+ }
+
+
+ WaitJob::~WaitJob()
+ {}
+
+ void WaitJob::kill(bool)
+ {
+ m_error = 0;
+ emitResult();
+ }
+
+ void WaitJob::timerDone()
+ {
+ // set the error to null and emit the result
+ m_error = 0;
+ emitResult();
+ }
+
+ void WaitJob::addExitOperation(kt::ExitOperation* op)
+ {
+ exit_ops.append(op);
+ connect(op,SIGNAL(operationFinished( kt::ExitOperation* )),
+ this,SLOT(operationFinished( kt::ExitOperation* )));
+ }
+
+ void WaitJob::operationFinished(kt::ExitOperation* op)
+ {
+ if (exit_ops.count() > 0)
+ {
+ exit_ops.remove(op);
+ if (op->deleteAllowed())
+ op->deleteLater();
+
+ if (exit_ops.count() == 0)
+ timerDone();
+ }
+ }
+
+ void WaitJob::execute(WaitJob* job)
+ {
+ KIO::NetAccess::synchronousRun(job,0);
+ }
+
+ void SynchronousWait(Uint32 millis)
+ {
+ kdDebug() << "SynchronousWait" << endl;
+ WaitJob* j = new WaitJob(millis);
+ KIO::NetAccess::synchronousRun(j,0);
+ }
+
+
+// void UpdateCurrentTime()
+// {
+// global_time_stamp = Now();
+// }
+
+}
+
+#include "upnprouter.moc"
diff --git a/src/upnp/upnprouter.h b/src/upnp/upnprouter.h
new file mode 100644
index 0000000..d717a49
--- /dev/null
+++ b/src/upnp/upnprouter.h
@@ -0,0 +1,303 @@
+/***************************************************************************
+ * 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 <qtimer.h>
+#include <kio/job.h>
+#include <qvaluelist.h>
+#include "exitoperation.h"
+#include <kurl.h>
+#include <qstringlist.h>
+#include <kstreamsocket.h>
+#include "portlist.h"
+#include "forwardportlist.h"
+
+using bt::Uint16;
+
+namespace bt
+{
+
+ /**
+ * @author Joris Guisson <joris.guisson@gmail.com>
+ *
+ * Job to wait for a certain amount of time or until one or more ExitOperation's have
+ * finished.
+ */
+ class WaitJob : public KIO::Job
+ {
+ Q_OBJECT
+ public:
+ WaitJob(Uint32 millis);
+ virtual ~WaitJob();
+
+ virtual void kill(bool quietly=true);
+
+ /**
+ * Add an ExitOperation;
+ * @param op The operation
+ */
+ void addExitOperation(kt::ExitOperation* op);
+
+
+ /**
+ * Execute a WaitJob
+ * @param job The Job
+ */
+ static void execute(WaitJob* job);
+
+ /// Are there any ExitOperation's we need to wait for
+ bool needToWait() const {return exit_ops.count() > 0;}
+
+ private slots:
+ void timerDone();
+ void operationFinished(kt::ExitOperation* op);
+
+ private:
+ QTimer timer;
+ QValueList<kt::ExitOperation*> exit_ops;
+ };
+
+ void SynchronousWait(Uint32 millis);
+
+
+}
+
+namespace bt
+{
+ class HTTPRequest;
+ class WaitJob;
+}
+
+namespace net
+{
+ class ForwardPortList;
+}
+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 extport;
+ net::Port intport;
+ bt::HTTPRequest* pending_req;
+ UPnPService* service;
+ };
+
+ struct ForwardingRequest
+ {
+ net::Port extport;
+ net::Port intport;
+ bt::HTTPRequest* pending_req;
+ };
+
+ private:
+ QString server;
+ QString tmp_file;
+ KURL location;
+ UPnPDeviceDescription desc;
+ QValueList<UPnPService> services;
+ QValueList<Forwarding> fwds;
+ QValueList<ForwardingRequest> fwdreqs;
+ QValueList<bt::HTTPRequest*> active_reqs;
+ net::ForwardPortList* forwardedPortList;
+
+ 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 & externalport,
+ const net::Port & internalport = net::Port::Port(),
+ bool force = false);
+
+ /**
+ * 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 & externalport,const net::Port & internalport,
+ bt::WaitJob* waitjob = 0);
+
+ void debugPrintData();
+
+ QValueList<ForwardingRequest>::iterator beginReqMappings() {return fwdreqs.begin();}
+ QValueList<ForwardingRequest>::iterator endReqMappings() {return fwdreqs.end();}
+ QValueList<Forwarding>::iterator beginPortMappings() {return fwds.begin();}
+ QValueList<Forwarding>::iterator endPortMappings() {return fwds.end();}
+ net::ForwardPortList* forwardedPorts() {return forwardedPortList;}
+ private slots:
+ void onReplyOK(bt::HTTPRequest* r,const QString &,bool);
+ void onReplyError(bt::HTTPRequest* r,const QString &,bool);
+ 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);
+
+ void replyOK(kt::UPnPRouter*,bt::HTTPRequest* ,const QString &,bool);
+ void replyError(kt::UPnPRouter*,bt::HTTPRequest* ,const QString &,bool);
+
+ private:
+ QValueList<UPnPService>::iterator findPortForwardingService();
+
+ bt::HTTPRequest* sendSoapQuery(const QString & query,const QString & soapact,const QString & controlurl,bool fwd, bool at_exit = false );
+ bool verbose;
+
+ void forward(UPnPService* srv,const net::Port & externalport,const net::Port & internalport = net::Port::Port());
+ void undoForward(UPnPService* srv,const net::Port & externalport,const net::Port &
+ internalport,bt::WaitJob* waitjob);
+ void httpRequestDone(bt::HTTPRequest* r,bool erase_fwd);
+ };
+
+}
+
+
+#endif