Some work about adaptive color scheme for Web UI (PR #19901)
http://[316:c51a:62a3:8b9::4]/d4708/qBittorrent/src/branch/adaptive-webui
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
7.9 KiB
248 lines
7.9 KiB
/* |
|
* Bittorrent Client using Qt4 and libtorrent. |
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru> |
|
* 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 <vector> |
|
#include <libtorrent/bencode.hpp> |
|
|
|
#include "preferences.h" |
|
#include "http/server.h" |
|
#include "qtracker.h" |
|
|
|
// QPeer |
|
bool QPeer::operator!=(const QPeer &other) const |
|
{ |
|
return qhash() != other.qhash(); |
|
} |
|
|
|
bool QPeer::operator==(const QPeer &other) const |
|
{ |
|
return qhash() == other.qhash(); |
|
} |
|
|
|
QString QPeer::qhash() const |
|
{ |
|
return ip + ":" + QString::number(port); |
|
} |
|
|
|
libtorrent::entry QPeer::toEntry(bool no_peer_id) const |
|
{ |
|
libtorrent::entry::dictionary_type peer_map; |
|
if (!no_peer_id) |
|
peer_map["id"] = libtorrent::entry(peer_id.toStdString()); |
|
peer_map["ip"] = libtorrent::entry(ip.toStdString()); |
|
peer_map["port"] = libtorrent::entry(port); |
|
|
|
return libtorrent::entry(peer_map); |
|
} |
|
|
|
// QTracker |
|
|
|
QTracker::QTracker(QObject *parent) |
|
: Http::ResponseBuilder(parent) |
|
, m_server(new Http::Server(this, this)) |
|
{ |
|
} |
|
|
|
QTracker::~QTracker() |
|
{ |
|
if (m_server->isListening()) |
|
qDebug("Shutting down the embedded tracker..."); |
|
// TODO: Store the torrent list |
|
} |
|
|
|
bool QTracker::start() |
|
{ |
|
const int listen_port = Preferences::instance()->getTrackerPort(); |
|
|
|
if (m_server->isListening()) { |
|
if (m_server->serverPort() == listen_port) { |
|
// Already listening on the right port, just return |
|
return true; |
|
} |
|
// Wrong port, closing the server |
|
m_server->close(); |
|
} |
|
|
|
qDebug("Starting the embedded tracker..."); |
|
// Listen on the predefined port |
|
return m_server->listen(QHostAddress::Any, listen_port); |
|
} |
|
|
|
Http::Response QTracker::processRequest(const Http::Request &request, const Http::Environment &env) |
|
{ |
|
clear(); // clear response |
|
|
|
//qDebug("QTracker received the following request:\n%s", qPrintable(parser.toString())); |
|
// Is request a GET request? |
|
if (request.method != "GET") { |
|
qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(request.method)); |
|
status(100, "Invalid request type"); |
|
} |
|
else if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) { |
|
qDebug("QTracker: Unrecognized path: %s", qPrintable(request.path)); |
|
status(100, "Invalid request type"); |
|
} |
|
else { |
|
// OK, this is a GET request |
|
m_request = request; |
|
m_env = env; |
|
respondToAnnounceRequest(); |
|
} |
|
|
|
return response(); |
|
} |
|
|
|
void QTracker::respondToAnnounceRequest() |
|
{ |
|
const QStringMap &gets = m_request.gets; |
|
TrackerAnnounceRequest annonce_req; |
|
|
|
// IP |
|
annonce_req.peer.ip = m_env.clientAddress.toString(); |
|
|
|
// 1. Get info_hash |
|
if (!gets.contains("info_hash")) { |
|
qDebug("QTracker: Missing info_hash"); |
|
status(101, "Missing info_hash"); |
|
return; |
|
} |
|
annonce_req.info_hash = gets.value("info_hash"); |
|
// info_hash cannot be longer than 20 bytes |
|
/*if (annonce_req.info_hash.toLatin1().length() > 20) { |
|
qDebug("QTracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length()); |
|
status(150, "Invalid infohash"); |
|
return; |
|
}*/ |
|
|
|
// 2. Get peer ID |
|
if (!gets.contains("peer_id")) { |
|
qDebug("QTracker: Missing peer_id"); |
|
status(102, "Missing peer_id"); |
|
return; |
|
} |
|
annonce_req.peer.peer_id = gets.value("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)); |
|
status(151, "Invalid peerid"); |
|
return; |
|
}*/ |
|
|
|
// 3. Get port |
|
if (!gets.contains("port")) { |
|
qDebug("QTracker: Missing port"); |
|
status(103, "Missing port"); |
|
return; |
|
} |
|
bool ok = false; |
|
annonce_req.peer.port = gets.value("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); |
|
status(103, "Missing port"); |
|
return; |
|
} |
|
|
|
// 4. Get event |
|
annonce_req.event = ""; |
|
if (gets.contains("event")) { |
|
annonce_req.event = gets.value("event"); |
|
qDebug("QTracker: event is %s", qPrintable(annonce_req.event)); |
|
} |
|
|
|
// 5. Get numwant |
|
annonce_req.numwant = 50; |
|
if (gets.contains("numwant")) { |
|
int tmp = gets.value("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 (gets.contains("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(annonce_req); |
|
} |
|
|
|
void QTracker::replyWithPeerList(const TrackerAnnounceRequest &annonce_req) |
|
{ |
|
// Prepare the entry for bencoding |
|
libtorrent::entry::dictionary_type reply_dict; |
|
reply_dict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL); |
|
QList<QPeer> peers = m_torrents.value(annonce_req.info_hash).values(); |
|
libtorrent::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"] = libtorrent::entry(peer_list); |
|
libtorrent::entry reply_entry(reply_dict); |
|
// bencode |
|
std::vector<char> buf; |
|
libtorrent::bencode(std::back_inserter(buf), reply_entry); |
|
QByteArray reply(&buf[0], static_cast<int>(buf.size())); |
|
qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData()); |
|
|
|
// HTTP reply |
|
print(reply, Http::CONTENT_TYPE_TXT); |
|
} |
|
|
|
|
|
|