mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-22 12:34:19 +00:00
FEATURE: qBittorrent can now act as a tracker
This commit is contained in:
parent
f6172f8c77
commit
aff27558dd
@ -1,4 +1,5 @@
|
|||||||
* Unreleased - Christophe Dumez <chris@qbittorrent.org> - v2.5.0
|
* Unreleased - Christophe Dumez <chris@qbittorrent.org> - v2.5.0
|
||||||
|
- FEATURE: qBittorrent can now act as a tracker
|
||||||
- FEATURE: Added feature to shutdown qbittorrent on torrents completion
|
- FEATURE: Added feature to shutdown qbittorrent on torrents completion
|
||||||
- FEATURE: Added a transfer list column to display the current tracker
|
- FEATURE: Added a transfer list column to display the current tracker
|
||||||
- COSMETIC: Replaced message box by on-screen notification for download errors
|
- COSMETIC: Replaced message box by on-screen notification for download errors
|
||||||
|
@ -11,15 +11,15 @@
|
|||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
|
|
||||||
enum AdvSettingsCols {PROPERTY, VALUE};
|
enum AdvSettingsCols {PROPERTY, VALUE};
|
||||||
enum AdvSettingsRows {DISK_CACHE, OUTGOING_PORT_MIN, OUTGOING_PORT_MAX, IGNORE_LIMIT_LAN, COUNT_OVERHEAD, RECHECK_COMPLETED, LIST_REFRESH, RESOLVE_COUNTRIES, RESOLVE_HOSTS, MAX_HALF_OPEN, SUPER_SEEDING, NETWORK_IFACE, PROGRAM_NOTIFICATIONS };
|
enum AdvSettingsRows {DISK_CACHE, OUTGOING_PORT_MIN, OUTGOING_PORT_MAX, IGNORE_LIMIT_LAN, COUNT_OVERHEAD, RECHECK_COMPLETED, LIST_REFRESH, RESOLVE_COUNTRIES, RESOLVE_HOSTS, MAX_HALF_OPEN, SUPER_SEEDING, NETWORK_IFACE, PROGRAM_NOTIFICATIONS, TRACKER_STATUS, TRACKER_PORT };
|
||||||
#define ROW_COUNT 13
|
#define ROW_COUNT 15
|
||||||
|
|
||||||
class AdvancedSettings: public QTableWidget {
|
class AdvancedSettings: public QTableWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QSpinBox *spin_cache, *outgoing_ports_min, *outgoing_ports_max, *spin_list_refresh, *spin_maxhalfopen;
|
QSpinBox *spin_cache, *outgoing_ports_min, *outgoing_ports_max, *spin_list_refresh, *spin_maxhalfopen, *spin_tracker_port;
|
||||||
QCheckBox *cb_ignore_limits_lan, *cb_count_overhead, *cb_recheck_completed, *cb_resolve_countries, *cb_resolve_hosts, *cb_super_seeding, *cb_program_notifications;
|
QCheckBox *cb_ignore_limits_lan, *cb_count_overhead, *cb_recheck_completed, *cb_resolve_countries, *cb_resolve_hosts, *cb_super_seeding, *cb_program_notifications, *cb_tracker_status;
|
||||||
QComboBox *combo_iface;
|
QComboBox *combo_iface;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -53,6 +53,8 @@ public:
|
|||||||
delete cb_super_seeding;
|
delete cb_super_seeding;
|
||||||
delete combo_iface;
|
delete combo_iface;
|
||||||
delete cb_program_notifications;
|
delete cb_program_notifications;
|
||||||
|
delete spin_tracker_port;
|
||||||
|
delete cb_tracker_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
@ -88,6 +90,9 @@ public slots:
|
|||||||
}
|
}
|
||||||
// Program notification
|
// Program notification
|
||||||
Preferences::useProgramNotification(cb_program_notifications->isChecked());
|
Preferences::useProgramNotification(cb_program_notifications->isChecked());
|
||||||
|
// Tracker
|
||||||
|
Preferences::setTrackerEnabled(cb_tracker_status->isChecked());
|
||||||
|
Preferences::setTrackerPort(spin_tracker_port->value());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
@ -195,6 +200,20 @@ protected slots:
|
|||||||
connect(cb_program_notifications, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
|
connect(cb_program_notifications, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
|
||||||
cb_program_notifications->setChecked(Preferences::useProgramNotification());
|
cb_program_notifications->setChecked(Preferences::useProgramNotification());
|
||||||
setCellWidget(PROGRAM_NOTIFICATIONS, VALUE, cb_program_notifications);
|
setCellWidget(PROGRAM_NOTIFICATIONS, VALUE, cb_program_notifications);
|
||||||
|
// Tracker State
|
||||||
|
setItem(TRACKER_STATUS, PROPERTY, new QTableWidgetItem(tr("Enable embedded tracker")));
|
||||||
|
cb_tracker_status = new QCheckBox();
|
||||||
|
connect(cb_tracker_status, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
|
||||||
|
cb_tracker_status->setChecked(Preferences::isTrackerEnabled());
|
||||||
|
setCellWidget(TRACKER_STATUS, VALUE, cb_tracker_status);
|
||||||
|
// Tracker port
|
||||||
|
setItem(TRACKER_PORT, PROPERTY, new QTableWidgetItem(tr("Embedded tracker port")));
|
||||||
|
spin_tracker_port = new QSpinBox();
|
||||||
|
connect(spin_tracker_port, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
|
||||||
|
spin_tracker_port->setMinimum(1);
|
||||||
|
spin_tracker_port->setMaximum(65535);
|
||||||
|
spin_tracker_port->setValue(Preferences::getTrackerPort());
|
||||||
|
setCellWidget(TRACKER_PORT, VALUE, spin_tracker_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void emitSettingsChanged() {
|
void emitSettingsChanged() {
|
||||||
|
@ -1192,6 +1192,26 @@ public:
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static bool isTrackerEnabled() {
|
||||||
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
return settings.value(QString::fromUtf8("Preferences/Advanced/trackerEnabled"), false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setTrackerEnabled(bool enabled) {
|
||||||
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
settings.setValue(QString::fromUtf8("Preferences/Advanced/trackerEnabled"), enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getTrackerPort() {
|
||||||
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
return settings.value(QString::fromUtf8("Preferences/Advanced/trackerPort"), 9000).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setTrackerPort(int port) {
|
||||||
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
settings.setValue(QString::fromUtf8("Preferences/Advanced/trackerPort"), port);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PREFERENCES_H
|
#endif // PREFERENCES_H
|
||||||
|
@ -84,6 +84,7 @@ Bittorrent::Bittorrent()
|
|||||||
, geoipDBLoaded(false), resolve_countries(false)
|
, geoipDBLoaded(false), resolve_countries(false)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
m_tracker = 0;
|
||||||
// To avoid some exceptions
|
// To avoid some exceptions
|
||||||
fs::path::default_name_check(fs::no_check);
|
fs::path::default_name_check(fs::no_check);
|
||||||
// For backward compatibility
|
// For backward compatibility
|
||||||
@ -171,6 +172,8 @@ Bittorrent::~Bittorrent() {
|
|||||||
delete s;
|
delete s;
|
||||||
}
|
}
|
||||||
// Delete our objects
|
// Delete our objects
|
||||||
|
if(m_tracker)
|
||||||
|
delete m_tracker;
|
||||||
delete timerAlerts;
|
delete timerAlerts;
|
||||||
if(BigRatioTimer)
|
if(BigRatioTimer)
|
||||||
delete BigRatioTimer;
|
delete BigRatioTimer;
|
||||||
@ -594,6 +597,21 @@ void Bittorrent::configureSession() {
|
|||||||
http_proxySettings.type = proxy_settings::none;
|
http_proxySettings.type = proxy_settings::none;
|
||||||
}
|
}
|
||||||
setHTTPProxySettings(http_proxySettings);
|
setHTTPProxySettings(http_proxySettings);
|
||||||
|
// Tracker
|
||||||
|
if(Preferences::isTrackerEnabled()) {
|
||||||
|
if(!m_tracker) {
|
||||||
|
m_tracker = new QTracker(this);
|
||||||
|
}
|
||||||
|
if(m_tracker->start()) {
|
||||||
|
addConsoleMessage(tr("Embedded Tracker [ON]"), QString::fromUtf8("blue"));
|
||||||
|
} else {
|
||||||
|
addConsoleMessage(tr("Failed to start the embedded tracker!"), QString::fromUtf8("red"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addConsoleMessage(tr("Embedded Tracker [OFF]"));
|
||||||
|
if(m_tracker)
|
||||||
|
delete m_tracker;
|
||||||
|
}
|
||||||
qDebug("Session configured");
|
qDebug("Session configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
#include <libtorrent/session.hpp>
|
#include <libtorrent/session.hpp>
|
||||||
#include <libtorrent/ip_filter.hpp>
|
#include <libtorrent/ip_filter.hpp>
|
||||||
|
|
||||||
|
#include "qtracker.h"
|
||||||
#include "qtorrenthandle.h"
|
#include "qtorrenthandle.h"
|
||||||
#include "trackerinfos.h"
|
#include "trackerinfos.h"
|
||||||
|
|
||||||
@ -250,6 +251,8 @@ private:
|
|||||||
bool geoipDBLoaded;
|
bool geoipDBLoaded;
|
||||||
bool resolve_countries;
|
bool resolve_countries;
|
||||||
#endif
|
#endif
|
||||||
|
// Tracker
|
||||||
|
QPointer<QTracker> m_tracker;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -355,6 +355,7 @@ contains(DEFINES, USE_SYSTEM_QTSINGLEAPPLICATION) {
|
|||||||
include(qtlibtorrent/qtlibtorrent.pri)
|
include(qtlibtorrent/qtlibtorrent.pri)
|
||||||
include(webui/webui.pri)
|
include(webui/webui.pri)
|
||||||
include(rss/rss.pri)
|
include(rss/rss.pri)
|
||||||
|
include(tracker/tracker.pri)
|
||||||
|
|
||||||
!contains(DEFINES, DISABLE_GUI) {
|
!contains(DEFINES, DISABLE_GUI) {
|
||||||
FORMS += ui/mainwindow.ui \
|
FORMS += ui/mainwindow.ui \
|
||||||
|
37
src/tracker/qpeer.h
Normal file
37
src/tracker/qpeer.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef QPEER_H
|
||||||
|
#define QPEER_H
|
||||||
|
|
||||||
|
#include <libtorrent/entry.hpp>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
using namespace libtorrent;
|
||||||
|
|
||||||
|
struct QPeer {
|
||||||
|
|
||||||
|
bool operator!=(const QPeer &other) const {
|
||||||
|
return qhash() != other.qhash();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const QPeer &other) const {
|
||||||
|
return qhash() == other.qhash();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString qhash() const {
|
||||||
|
return ip+":"+QString::number(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry toEntry(bool no_peer_id) const {
|
||||||
|
entry::dictionary_type peer_map;
|
||||||
|
if(!no_peer_id)
|
||||||
|
peer_map["id"] = entry(peer_id.toStdString());
|
||||||
|
peer_map["ip"] = entry(ip.toStdString());
|
||||||
|
peer_map["port"] = entry(port);
|
||||||
|
return entry(peer_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ip;
|
||||||
|
QString peer_id;
|
||||||
|
int port;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QPEER_H
|
236
src/tracker/qtracker.cpp
Normal file
236
src/tracker/qtracker.cpp
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QHttpRequestHeader>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
#include <libtorrent/bencode.hpp>
|
||||||
|
#include <libtorrent/entry.hpp>
|
||||||
|
|
||||||
|
#include "qtracker.h"
|
||||||
|
#include "preferences.h"
|
||||||
|
|
||||||
|
using namespace libtorrent;
|
||||||
|
|
||||||
|
QTracker::QTracker(QObject *parent) :
|
||||||
|
QTcpServer(parent)
|
||||||
|
{
|
||||||
|
Q_ASSERT(Preferences::isTrackerEnabled());
|
||||||
|
connect(this, SIGNAL(newConnection()), this, SLOT(handlePeerConnection()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QTracker::~QTracker() {
|
||||||
|
if(isListening()) {
|
||||||
|
qDebug("Shutting down the embedded tracker...");
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
// TODO: Store the torrent list
|
||||||
|
}
|
||||||
|
|
||||||
|
void QTracker::handlePeerConnection()
|
||||||
|
{
|
||||||
|
QTcpSocket *socket;
|
||||||
|
while((socket = nextPendingConnection()))
|
||||||
|
{
|
||||||
|
qDebug("QTracker: New peer connection");
|
||||||
|
connect(socket, SIGNAL(readyRead()), SLOT(readRequest()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QTracker::start()
|
||||||
|
{
|
||||||
|
const int listen_port = Preferences::getTrackerPort();
|
||||||
|
//
|
||||||
|
if(isListening()) {
|
||||||
|
if(serverPort() == listen_port) {
|
||||||
|
// Already listening on the right port, just return
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Wrong port, closing the server
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
qDebug("Starting the embedded tracker...");
|
||||||
|
// Listen on the predefined port
|
||||||
|
return listen(QHostAddress::Any, listen_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QTracker::readRequest()
|
||||||
|
{
|
||||||
|
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
|
||||||
|
QByteArray input = socket->readAll();
|
||||||
|
//qDebug("QTracker: Raw request:\n%s", input.data());
|
||||||
|
HttpRequestParser parser;
|
||||||
|
parser.write(input);
|
||||||
|
if(!parser.isValid()) {
|
||||||
|
qDebug("QTracker: Invalid HTTP Request:\n %s", qPrintable(parser.toString()));
|
||||||
|
respondInvalidRequest(socket, 100, "Invalid request type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//qDebug("QTracker received the following request:\n%s", qPrintable(parser.toString()));
|
||||||
|
// Request is correct, is it a GET request?
|
||||||
|
if(parser.method() != "GET") {
|
||||||
|
qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(parser.method()));
|
||||||
|
respondInvalidRequest(socket, 100, "Invalid request type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!parser.url().startsWith("/announce", Qt::CaseInsensitive)) {
|
||||||
|
qDebug("QTracker: Unrecognized path: %s", qPrintable(parser.url()));
|
||||||
|
respondInvalidRequest(socket, 100, "Invalid request type");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// OK, this is a GET request
|
||||||
|
respondToAnnounceRequest(socket, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QTracker::respondInvalidRequest(QTcpSocket *socket, int code, QString msg)
|
||||||
|
{
|
||||||
|
QHttpResponseHeader response;
|
||||||
|
response.setStatusLine(code, msg);
|
||||||
|
socket->write(response.toString().toLocal8Bit());
|
||||||
|
socket->disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QTracker::respondToAnnounceRequest(QTcpSocket *socket, const HttpRequestParser& parser)
|
||||||
|
{
|
||||||
|
TrackerAnnounceRequest annonce_req;
|
||||||
|
// IP
|
||||||
|
annonce_req.peer.ip = socket->peerAddress().toString();
|
||||||
|
// 1. Get info_hash
|
||||||
|
if(parser.get("info_hash").isNull()) {
|
||||||
|
qDebug("QTracker: Missing info_hash");
|
||||||
|
respondInvalidRequest(socket, 101, "Missing info_hash");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
annonce_req.info_hash = parser.get("info_hash");
|
||||||
|
// info_hash cannot be longer than 20 bytes
|
||||||
|
if(annonce_req.info_hash.toAscii().length() > 20) {
|
||||||
|
qDebug("QTracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toAscii().length());
|
||||||
|
respondInvalidRequest(socket, 150, "Invalid infohash");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 2. Get peer ID
|
||||||
|
if(parser.get("peer_id").isNull()) {
|
||||||
|
qDebug("QTracker: Missing peer_id");
|
||||||
|
respondInvalidRequest(socket, 102, "Missing peer_id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
annonce_req.peer.peer_id = parser.get("peer_id");
|
||||||
|
// peer_id cannot be longer than 20 bytes
|
||||||
|
if(annonce_req.peer.peer_id.length() > 20) {
|
||||||
|
qDebug("QTracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id));
|
||||||
|
respondInvalidRequest(socket, 151, "Invalid peerid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 3. Get port
|
||||||
|
if(parser.get("port").isNull()) {
|
||||||
|
qDebug("QTracker: Missing port");
|
||||||
|
respondInvalidRequest(socket, 103, "Missing port");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool ok = false;
|
||||||
|
annonce_req.peer.port = parser.get("port").toInt(&ok);
|
||||||
|
if(!ok || annonce_req.peer.port < 1 || annonce_req.peer.port > 65535) {
|
||||||
|
qDebug("QTracker: Invalid port number (%d)", annonce_req.peer.port);
|
||||||
|
respondInvalidRequest(socket, 103, "Missing port");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 4. Get event
|
||||||
|
annonce_req.event = "";
|
||||||
|
if(!parser.get("event").isNull()) {
|
||||||
|
annonce_req.event = parser.get("event");
|
||||||
|
qDebug("QTracker: event is %s", qPrintable(annonce_req.event));
|
||||||
|
}
|
||||||
|
// 5. Get numwant
|
||||||
|
annonce_req.numwant = 50;
|
||||||
|
if(!parser.get("numwant").isNull()) {
|
||||||
|
int tmp = parser.get("numwant").toInt();
|
||||||
|
if(tmp > 0) {
|
||||||
|
qDebug("QTracker: numwant=%d", tmp);
|
||||||
|
annonce_req.numwant = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 6. no_peer_id (extension)
|
||||||
|
annonce_req.no_peer_id = false;
|
||||||
|
if(parser.hasKey("no_peer_id")) {
|
||||||
|
annonce_req.no_peer_id = true;
|
||||||
|
}
|
||||||
|
// 7. TODO: support "compact" extension
|
||||||
|
// Done parsing, now let's reply
|
||||||
|
if(m_torrents.contains(annonce_req.info_hash)) {
|
||||||
|
if(annonce_req.event == "stopped") {
|
||||||
|
qDebug("QTracker: Peer stopped downloading, deleting it from the list");
|
||||||
|
m_torrents[annonce_req.info_hash].remove(annonce_req.peer.qhash());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unknown torrent
|
||||||
|
if(m_torrents.size() == MAX_TORRENTS) {
|
||||||
|
// Reached max size, remove a random torrent
|
||||||
|
m_torrents.erase(m_torrents.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Register the user
|
||||||
|
PeerList peers = m_torrents.value(annonce_req.info_hash);
|
||||||
|
if(peers.size() == MAX_PEERS_PER_TORRENT) {
|
||||||
|
// Too many peers, remove a random one
|
||||||
|
peers.erase(peers.begin());
|
||||||
|
}
|
||||||
|
peers[annonce_req.peer.qhash()] = annonce_req.peer;
|
||||||
|
m_torrents[annonce_req.info_hash] = peers;
|
||||||
|
// Reply
|
||||||
|
ReplyWithPeerList(socket, annonce_req);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QTracker::ReplyWithPeerList(QTcpSocket *socket, const TrackerAnnounceRequest &annonce_req)
|
||||||
|
{
|
||||||
|
// Prepare the entry for bencoding
|
||||||
|
entry::dictionary_type reply_dict;
|
||||||
|
reply_dict["interval"] = entry(ANNOUNCE_INTERVAL);
|
||||||
|
QList<QPeer> peers = m_torrents.value(annonce_req.info_hash).values();
|
||||||
|
entry::list_type peer_list;
|
||||||
|
foreach(const QPeer & p, peers) {
|
||||||
|
//if(p != annonce_req.peer)
|
||||||
|
peer_list.push_back(p.toEntry(annonce_req.no_peer_id));
|
||||||
|
}
|
||||||
|
reply_dict["peers"] = entry(peer_list);
|
||||||
|
entry reply_entry(reply_dict);
|
||||||
|
// bencode
|
||||||
|
std::vector<char> buf;
|
||||||
|
bencode(std::back_inserter(buf), reply_entry);
|
||||||
|
QByteArray reply(buf.data(), buf.size());
|
||||||
|
qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData());
|
||||||
|
// HTTP reply
|
||||||
|
QHttpResponseHeader response;
|
||||||
|
response.setStatusLine(200, "OK");
|
||||||
|
socket->write(response.toString().toLocal8Bit() + reply);
|
||||||
|
socket->disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
72
src/tracker/qtracker.h
Normal file
72
src/tracker/qtracker.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2010 Christophe Dumez
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QTRACKER_H
|
||||||
|
#define QTRACKER_H
|
||||||
|
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <QHttpResponseHeader>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
#include "httprequestparser.h"
|
||||||
|
#include "trackerannouncerequest.h"
|
||||||
|
#include "qpeer.h"
|
||||||
|
|
||||||
|
// static limits
|
||||||
|
const int MAX_TORRENTS = 100;
|
||||||
|
const int MAX_PEERS_PER_TORRENT = 1000;
|
||||||
|
const int ANNOUNCE_INTERVAL = 1800; // 30min
|
||||||
|
|
||||||
|
typedef QHash<QString, QPeer> PeerList;
|
||||||
|
typedef QHash<QString, PeerList> TorrentList;
|
||||||
|
|
||||||
|
/* Basic Bittorrent tracker implementation in Qt4 */
|
||||||
|
/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */
|
||||||
|
class QTracker : public QTcpServer
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit QTracker(QObject *parent = 0);
|
||||||
|
~QTracker();
|
||||||
|
bool start();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void readRequest();
|
||||||
|
void handlePeerConnection();
|
||||||
|
void respondInvalidRequest(QTcpSocket *socket, int code, QString msg);
|
||||||
|
void respondToAnnounceRequest(QTcpSocket *socket, const HttpRequestParser& parser);
|
||||||
|
void ReplyWithPeerList(QTcpSocket *socket, const TrackerAnnounceRequest &annonce_req);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TorrentList m_torrents;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // QTRACKER_H
|
9
src/tracker/tracker.pri
Normal file
9
src/tracker/tracker.pri
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/qtracker.h \
|
||||||
|
$$PWD/trackerannouncerequest.h \
|
||||||
|
$$PWD/qpeer.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/qtracker.cpp
|
15
src/tracker/trackerannouncerequest.h
Normal file
15
src/tracker/trackerannouncerequest.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef TRACKERANNOUNCEREQUEST_H
|
||||||
|
#define TRACKERANNOUNCEREQUEST_H
|
||||||
|
|
||||||
|
#include <qpeer.h>
|
||||||
|
|
||||||
|
struct TrackerAnnounceRequest {
|
||||||
|
QString info_hash;
|
||||||
|
QString event;
|
||||||
|
int numwant;
|
||||||
|
QPeer peer;
|
||||||
|
// Extensions
|
||||||
|
bool no_peer_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TRACKERANNOUNCEREQUEST_H
|
@ -68,12 +68,12 @@ QByteArray HttpRequestParser::message() const
|
|||||||
|
|
||||||
QString HttpRequestParser::get(const QString key) const
|
QString HttpRequestParser::get(const QString key) const
|
||||||
{
|
{
|
||||||
return getMap[key];
|
return getMap.value(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString HttpRequestParser::post(const QString key) const
|
QString HttpRequestParser::post(const QString key) const
|
||||||
{
|
{
|
||||||
return postMap[key];
|
return postMap.value(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray HttpRequestParser::torrent() const
|
QByteArray HttpRequestParser::torrent() const
|
||||||
|
Loading…
x
Reference in New Issue
Block a user