diff --git a/src/download/httpconnection.cpp b/src/download/httpconnection.cpp index f73e8d6..730e801 100644 --- a/src/download/httpconnection.cpp +++ b/src/download/httpconnection.cpp @@ -1,432 +1,432 @@ /*************************************************************************** * Copyright (C) 2008 by Joris Guisson and Ivan Vasic * * joris.guisson@gmail.com * * ivasic@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 "httpconnection.h" #include #include #include #include #include #include #include #include "httpresponseheader.h" #include "version.h" namespace bt { HttpConnection::HttpConnection() : sock(0), state(IDLE), mutex(QMutex::Recursive), request(0), using_proxy(false), response_code(0) { status = i18n("Not connected"); connect(&reply_timer, &QTimer::timeout, this, &HttpConnection::replyTimeout); connect(&connect_timer, &QTimer::timeout, this, &HttpConnection::connectTimeout); connect(this,SIGNAL(startReplyTimer(int)),&reply_timer,SLOT(start(int)),Qt::QueuedConnection); connect(this,SIGNAL(stopReplyTimer()),&reply_timer,SLOT(stop()),Qt::QueuedConnection); connect(this,SIGNAL(stopConnectTimer()),&connect_timer,SLOT(stop()),Qt::QueuedConnection); up_gid = down_gid = 0; close_when_finished = false; redirected = false; } HttpConnection::~HttpConnection() { if (sock) { net::SocketMonitor::instance().remove(sock); delete sock; } delete request; } void HttpConnection::setGroupIDs(Uint32 up,Uint32 down) { up_gid = up; down_gid = down; if (sock) { sock->setGroupID(up_gid,true); sock->setGroupID(down_gid,false); } } const QString HttpConnection::getStatusString() const { QMutexLocker locker(&mutex); return status; } bool HttpConnection::ok() const { QMutexLocker locker(&mutex); return state != ERROR; } bool HttpConnection::connected() const { QMutexLocker locker(&mutex); return state == ACTIVE; } bool HttpConnection::closed() const { QMutexLocker locker(&mutex); return state == CLOSED || (sock && !sock->socketDevice()->ok()); } bool HttpConnection::ready() const { QMutexLocker locker(&mutex); return !request; } void HttpConnection::connectToProxy(const QString & proxy,Uint16 proxy_port) { if (OpenFileAllowed()) { using_proxy = true; net::AddressResolver::resolve(proxy, proxy_port, this, SLOT(hostResolved(net::AddressResolver*))); state = RESOLVING; status = i18n("Resolving proxy %1:%2",proxy,proxy_port); } else { Out(SYS_CON|LOG_IMPORTANT) << "HttpConnection: not enough system resources available" << endl; state = ERROR; status = i18n("Not enough system resources available"); } } void HttpConnection::connectTo(const QUrl &url) { if (OpenFileAllowed()) { using_proxy = false; net::AddressResolver::resolve(url.host(), url.port() <= 0 ? 80 : url.port(), this, SLOT(hostResolved(net::AddressResolver*))); state = RESOLVING; status = i18n("Resolving hostname %1",url.host()); } else { Out(SYS_CON|LOG_IMPORTANT) << "HttpConnection: not enough system resources available" << endl; state = ERROR; status = i18n("Not enough system resources available"); } } void HttpConnection::onDataReady(Uint8* buf,Uint32 size) { QMutexLocker locker(&mutex); if (state != ERROR && request) { if (size == 0) { // connection closed state = CLOSED; status = i18n("Connection closed"); } else { if (!request->onDataReady(buf,size)) { state = ERROR; status = i18n("Error: request failed: %1",request->failure_reason); response_code = request->response_code; } else if (request->response_header_received) stopReplyTimer(); } } } void HttpConnection::dataSent() { QMutexLocker locker(&mutex); if (state == ACTIVE && request) { request->buffer.clear(); // wait 60 seconds for a reply startReplyTimer(60 * 1000); } } void HttpConnection::connectFinished(bool succeeded) { QMutexLocker locker(&mutex); if (state == CONNECTING) { if (succeeded) { state = ACTIVE; status = i18n("Connected"); if (request && !request->request_sent) { sock->addData(request->buffer); request->request_sent = true; } } else { Out(SYS_CON|LOG_IMPORTANT) << "HttpConnection: failed to connect to webseed " << endl; state = ERROR; status = i18n("Error: Failed to connect to webseed"); } stopConnectTimer(); } } void HttpConnection::hostResolved(net::AddressResolver* ar) { if (ar->succeeded()) { net::Address addr = ar->address(); if (!sock) { sock = new net::StreamSocket(true, addr.ipVersion(), this); sock->socketDevice()->setBlocking(false); sock->setReader(this); sock->setGroupID(up_gid,true); sock->setGroupID(down_gid,false); } if (sock->socketDevice()->connectTo(addr)) { status = i18n("Connected"); state = ACTIVE; net::SocketMonitor::instance().add(sock); net::SocketMonitor::instance().signalPacketReady(); } else if (sock->socketDevice()->state() == net::SocketDevice::CONNECTING) { status = i18n("Connecting"); state = CONNECTING; net::SocketMonitor::instance().add(sock); net::SocketMonitor::instance().signalPacketReady(); // 60 second connect timeout connect_timer.start(60000); } else { Out(SYS_CON|LOG_IMPORTANT) << "HttpConnection: failed to connect to webseed" << endl; state = ERROR; status = i18n("Failed to connect to webseed"); } } else { Out(SYS_CON|LOG_IMPORTANT) << "HttpConnection: failed to resolve hostname of webseed" << endl; state = ERROR; status = i18n("Failed to resolve hostname of webseed"); } } bool HttpConnection::get(const QString & host,const QString & path,const QString & query,bt::Uint64 start,bt::Uint64 len) { QMutexLocker locker(&mutex); if (state == ERROR || request) return false; request = new HttpGet(host,path,query,start,len,using_proxy); if (sock) { sock->addData(request->buffer); request->request_sent = true; } return true; } bool HttpConnection::getData(QByteArray & data) { QMutexLocker locker(&mutex); if (!request) return false; HttpGet* g = request; if (g->redirected) { // wait until we have the entire content if we are redirected if (g->data_received < g->content_length) return false; // we have the content so we can redirect the connection redirected_url = g->redirected_to; redirected = true; return false; } if (g->piece_data.size() == 0) return false; data = g->piece_data; g->piece_data.clear(); // if all the data has been received and passed on to something else // remove the current request from the queue if (g->piece_data.size() == 0 && g->finished()) { delete g; request = 0; if (close_when_finished) { state = CLOSED; Out(SYS_CON|LOG_DEBUG) << "HttpConnection: closing connection due to redirection" << endl; // reset connection sock->socketDevice()->reset(); } } return true; } int HttpConnection::getDownloadRate() const { if (sock) { sock->updateSpeeds(bt::CurrentTime()); return sock->getDownloadRate(); } else return 0; } void HttpConnection::connectTimeout() { QMutexLocker locker(&mutex); if (state == CONNECTING) { status = i18n("Error: failed to connect, server not responding"); state = ERROR; } connect_timer.stop(); } void HttpConnection::replyTimeout() { QMutexLocker locker(&mutex); if (!request || !request->response_header_received) { status = i18n("Error: request timed out"); state = ERROR; reply_timer.stop(); } } //////////////////////////////////////////// HttpConnection::HttpGet::HttpGet(const QString & host,const QString & path,const QString & query,bt::Uint64 start,bt::Uint64 len,bool using_proxy) : host(host),path(path),query(query),start(start),len(len),data_received(0),response_header_received(false),request_sent(false),response_code(0) { QUrl url; url.setPath(path); url.setQuery(query); buffer.clear(); - buffer += QByteArrayLiteral("GET ") + (using_proxy?url.toEncoded():(url.path(QUrl::FullyEncoded).toLatin1() + '?' + url.query(QUrl::FullyEncoded).toLatin1())) + "HTTP/1.1\r\n" + buffer += QByteArrayLiteral("GET ") + (using_proxy?url.toEncoded():(url.path(QUrl::FullyEncoded).toLatin1() + '?' + url.query(QUrl::FullyEncoded).toLatin1())) + " HTTP/1.1\r\n" "Host: " + host.toLatin1() + "\r\n" "Range: bytes=" + QByteArray::number(start) + '-' + QByteArray::number(start + len - 1) + "\r\n" "User-Agent: " + bt::GetVersionString().toLatin1() + "\r\n" "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\n" "Accept-Language: en-us,en;q=0.5\r\n" "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" +(using_proxy? "Keep-Alive: 300\r\n" "Proxy-Connection: keep-alive\r\n\r\n" : "Connection: Keep-Alive\r\n\r\n"); redirected = false; content_length = 0; Out(SYS_CON|LOG_DEBUG) << "HttpConnection: sending http request:" << endl; Out(SYS_CON|LOG_DEBUG) << buffer.constData() << endl; } HttpConnection::HttpGet::~HttpGet() {} bool HttpConnection::HttpGet::onDataReady(Uint8* buf,Uint32 size) { if (!response_header_received) { // append the data buffer.append(QByteArray::fromRawData((char*)buf,size)); // look for the end of the header int idx = buffer.indexOf("\r\n\r\n"); if (idx == -1) // haven't got the full header yet return true; response_header_received = true; HttpResponseHeader hdr(QString::fromLatin1(buffer.mid(0,idx + 4))); if (hdr.hasKey("Content-Length")) content_length = hdr.value("Content-Length").toInt(); else content_length = 0; Out(SYS_CON|LOG_DEBUG) << "HttpConnection: http reply header received" << endl; Out(SYS_CON|LOG_DEBUG) << buffer.mid(0,idx + 4).constData() << endl; response_code = hdr.statusCode(); if ((hdr.statusCode() >= 300 && hdr.statusCode() <= 303) || hdr.statusCode() == 307) { // we got redirected to somewhere else if (!hdr.hasKey("Location")) { failure_reason = i18n("Redirected without a new location."); return false; } else { Out(SYS_CON|LOG_DEBUG) << "Redirected to " << hdr.value("Location") << endl; redirected = true; redirected_to = QUrl(hdr.value("Location")); } } else if (! (hdr.statusCode() == 200 || hdr.statusCode() == 206)) { failure_reason = hdr.reasonPhrase(); return false; } if (buffer.size() - (idx + 4) > 0) { // more data then the header has arrived so append it to piece_data data_received += buffer.size() - (idx + 4); piece_data.append(buffer.mid(idx + 4)); } } else { // append the data to the list data_received += size; piece_data.append(QByteArray((char*)buf,size)); } return true; } } diff --git a/src/net/streamsocket.cpp b/src/net/streamsocket.cpp index b69a609..2a6e6ce 100644 --- a/src/net/streamsocket.cpp +++ b/src/net/streamsocket.cpp @@ -1,87 +1,89 @@ /*************************************************************************** * Copyright (C) 2011 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 "streamsocket.h" #include "socketmonitor.h" namespace net { StreamSocket::StreamSocket(bool tcp, int ip_version, StreamSocketListener* listener) : TrafficShapedSocket(tcp, ip_version), listener(listener) { } StreamSocket::~StreamSocket() { } void StreamSocket::addData(const QByteArray& data) { QMutexLocker lock(&mutex); buffer.append(data); net::SocketMonitor::instance().signalPacketReady(); } bool StreamSocket::bytesReadyToWrite() const { QMutexLocker lock(&mutex); return !buffer.isEmpty() || sock->state() == net::SocketDevice::CONNECTING; } bt::Uint32 StreamSocket::write(bt::Uint32 max, bt::TimeStamp now) { Q_UNUSED(now); QMutexLocker lock(&mutex); if (sock->state() == net::SocketDevice::CONNECTING) { bool ok = sock->connectSuccesFull(); if (listener) listener->connectFinished(ok); if (!ok) return 0; } if (buffer.isEmpty()) return 0; - int to_send = qMin(buffer.size(), max); + + // max 0 means unlimited transfer, try to send the entire buffer then + int to_send = (max == 0) ? buffer.size() : qMin(buffer.size(), max); int ret = sock->send((const bt::Uint8*)buffer.data(), to_send); if (ret == to_send) { buffer.clear(); if (listener) listener->dataSent(); return ret; } else if (ret > 0) { buffer = buffer.mid(ret); return ret; } else { return 0; } } } diff --git a/src/upnp/upnprouter.cpp b/src/upnp/upnprouter.cpp index f51ebcd..5ae95b0 100644 --- a/src/upnp/upnprouter.cpp +++ b/src/upnp/upnprouter.cpp @@ -1,553 +1,573 @@ /*************************************************************************** * Copyright (C) 2005-2007 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "upnprouter.h" #include "upnpdescriptionparser.h" #include "soap.h" #include "httprequest.h" using namespace net; namespace bt { struct Forwarding { net::Port port; HTTPRequest* pending_req; const UPnPService* service; }; class UPnPRouter::UPnPRouterPrivate { public: UPnPRouterPrivate(const QString& server, const QUrl &location, bool verbose, UPnPRouter* parent); ~UPnPRouterPrivate(); HTTPRequest* sendSoapQuery(const QString& query, const QString& soapact, const QString& controlurl, bool at_exit = false); void forward(const UPnPService* srv, const net::Port& port); void undoForward(const UPnPService* srv, const net::Port& port, bt::WaitJob* waitjob); void httpRequestDone(HTTPRequest* r, bool erase_fwd); void getExternalIP(); public: QString server; QUrl location; UPnPDeviceDescription desc; QList services; QList fwds; QList active_reqs; QString error; bool verbose; UPnPRouter* parent; QString external_ip; }; //////////////////////////////////// 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 = ""; } 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 QUrl &location, bool verbose) : d(new UPnPRouterPrivate(server, location, verbose, this)) { } UPnPRouter::~UPnPRouter() { delete d; } void UPnPRouter::addService(const UPnPService& s) { +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) + foreach(const UPnPService& os, d->services) +#else for (const UPnPService& os : qAsConst(d->services)) +#endif { if(s.servicetype == os.servicetype) return; } d->services.append(s); } void UPnPRouter::downloadFinished(KJob* j) { if(j->error()) { d->error = i18n("Failed to download %1: %2", d->location.toDisplayString(), j->errorString()); Out(SYS_PNP | LOG_IMPORTANT) << d->error << endl; return; } KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j; // load in the file (target is always local) UPnPDescriptionParser desc_parse; bool ret = desc_parse.parse(st->data(), this); if(!ret) { d->error = i18n("Error parsing router description."); } xmlFileDownloaded(this, ret); d->getExternalIP(); } void UPnPRouter::downloadXMLFile() { d->error = QString(); // downlaod XML description into a temporary file in /tmp Out(SYS_PNP | LOG_DEBUG) << "Downloading XML file " << d->location << endl; KIO::Job* job = KIO::storedGet(d->location, KIO::NoReload, KIO::Overwrite | KIO::HideProgressInfo); connect(job, SIGNAL(result(KJob*)), this, SLOT(downloadFinished(KJob*))); } void UPnPRouter::forward(const net::Port& port) { if(!d->error.isEmpty()) { d->error = QString(); stateChanged(); } bool found = false; Out(SYS_PNP | LOG_NOTICE) << "Forwarding port " << port.number << " (" << (port.proto == UDP ? "UDP" : "TCP") << ")" << endl; // first find the right service +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) + foreach(const UPnPService& s, d->services) +#else for (const UPnPService& s : qAsConst(d->services)) +#endif { if(s.servicetype.contains("WANIPConnection") || s.servicetype.contains("WANPPPConnection")) { d->forward(&s, port); found = true; } } if(!found) { d->error = i18n("Forwarding failed:\nDevice does not have a WANIPConnection or WANPPPConnection."); Out(SYS_PNP | LOG_IMPORTANT) << d->error << endl; stateChanged(); } } 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; QList::iterator itr = d->fwds.begin(); while(itr != d->fwds.end()) { Forwarding& wd = *itr; if(wd.port == port) { d->undoForward(wd.service, wd.port, waitjob); itr = d->fwds.erase(itr); } else { ++itr; } } stateChanged(); } void UPnPRouter::forwardResult(HTTPRequest* r) { if(r->succeeded()) { d->httpRequestDone(r, false); } else { d->httpRequestDone(r, true); if(d->fwds.count() == 0) { d->error = r->errorString(); stateChanged(); } } } void UPnPRouter::undoForwardResult(HTTPRequest* r) { d->active_reqs.removeAll(r); r->deleteLater(); } void UPnPRouter::getExternalIPResult(HTTPRequest* r) { d->active_reqs.removeAll(r); if(r->succeeded()) { QDomDocument doc; if(!doc.setContent(r->replyData())) { Out(SYS_PNP | LOG_DEBUG) << "UPnP: GetExternalIP failed: invalid reply" << endl; } else { QDomNodeList nodes = doc.elementsByTagName("NewExternalIPAddress"); if(nodes.count() > 0) { d->external_ip = nodes.item(0).firstChild().nodeValue(); Out(SYS_PNP | LOG_DEBUG) << "UPnP: External IP: " << d->external_ip << endl; // Keep track of external IP so AccessManager can block it, makes no sense to connect to ourselves AccessManager::instance().addExternalIP(d->external_ip); } else Out(SYS_PNP | LOG_DEBUG) << "UPnP: GetExternalIP failed: no IP address returned" << endl; } } else { Out(SYS_PNP | LOG_DEBUG) << "UPnP: GetExternalIP failed: " << r->errorString() << endl; } r->deleteLater(); } QString UPnPRouter::getExternalIP() const { return d->external_ip; } #if 0 void UPnPRouter::isPortForwarded(const net::Port& port) { // first find the right service QList::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 QList 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 void UPnPRouter::setVerbose(bool v) { d->verbose = v; } QString UPnPRouter::getServer() const { return d->server; } QUrl UPnPRouter::getLocation() const { return d->location; } UPnPDeviceDescription& UPnPRouter::getDescription() { return d->desc; } const UPnPDeviceDescription& UPnPRouter::getDescription() const { return d->desc; } QString UPnPRouter::getError() const { return d->error; } void UPnPRouter::visit(UPnPRouter::Visitor* visitor) const { +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) + foreach(const Forwarding& fwd, d->fwds) +#else for (const Forwarding& fwd : qAsConst(d->fwds)) +#endif { visitor->forwarding(fwd.port, fwd.pending_req != 0, fwd.service); } } //////////////////////////////////// UPnPRouter::UPnPRouterPrivate::UPnPRouterPrivate(const QString& server, const QUrl &location, bool verbose, UPnPRouter* parent) : server(server), location(location), verbose(verbose), parent(parent) { } UPnPRouter::UPnPRouterPrivate::~UPnPRouterPrivate() { +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) + foreach(HTTPRequest* r, active_reqs) +#else for (HTTPRequest* r : qAsConst(active_reqs)) +#endif { r->deleteLater(); } } HTTPRequest* UPnPRouter::UPnPRouterPrivate::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); QUrl ctrlurl(controlurl); QString host = !ctrlurl.host().isEmpty() ? ctrlurl.host() : location.host(); bt::Uint16 port = ctrlurl.port() != -1 ? ctrlurl.port() : location.port(80); QNetworkRequest networkReq; networkReq.setUrl(ctrlurl); networkReq.setRawHeader("Host", host.toLatin1() + QByteArrayLiteral(":") + QByteArray::number(port)); networkReq.setRawHeader("User-Agent", bt::GetVersionString().toLatin1()); networkReq.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("text/xml")); networkReq.setRawHeader("SOAPAction", soapact.toLatin1()); HTTPRequest* r = new HTTPRequest(networkReq, query, host, port, verbose); if(!at_exit) { // Only listen for results when we are not exiting active_reqs.append(r); } return r; } void UPnPRouter::UPnPRouterPrivate::forward(const UPnPService* srv, const net::Port& port) { // add all the arguments for the command QList args; SOAP::Arg 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 == net::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 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 QList::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); connect(fw.pending_req, SIGNAL(result(HTTPRequest*)), parent, SLOT(forwardResult(HTTPRequest*))); fwds.append(fw); } void UPnPRouter::UPnPRouterPrivate::undoForward(const UPnPService* srv, const net::Port& port, bt::WaitJob* waitjob) { // add all the arguments for the command QList 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 == net::TCP ? "TCP" : "UDP"; args.append(a); QString action = "DeletePortMapping"; QString comm = SOAP::createCommand(action, srv->servicetype, args); HTTPRequest* r = sendSoapQuery(comm, srv->servicetype + "#" + action, srv->controlurl, waitjob != 0); if(waitjob) waitjob->addExitOperation(r); else connect(r, SIGNAL(result(HTTPRequest*)), parent, SLOT(undoForwardResult(HTTPRequest*))); } void UPnPRouter::UPnPRouterPrivate::getExternalIP() { +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) + foreach(const UPnPService& s, services) +#else for (const UPnPService& s : qAsConst(services)) +#endif { if(s.servicetype.contains("WANIPConnection") || s.servicetype.contains("WANPPPConnection")) { QString action = "GetExternalIPAddress"; QString comm = SOAP::createCommand(action, s.servicetype); HTTPRequest* r = sendSoapQuery(comm, s.servicetype + "#" + action, s.controlurl); connect(r, SIGNAL(result(HTTPRequest*)), parent, SLOT(getExternalIPResult(HTTPRequest*))); break; } } } void UPnPRouter::UPnPRouterPrivate::httpRequestDone(HTTPRequest* r, bool erase_fwd) { QList::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; } active_reqs.removeAll(r); r->deleteLater(); } }