examples/network/torrent/trackerclient.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
child 7 f7bc934e204c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/network/torrent/trackerclient.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "bencodeparser.h"
+#include "connectionmanager.h"
+#include "torrentclient.h"
+#include "torrentserver.h"
+#include "trackerclient.h"
+
+#include <QtCore>
+
+TrackerClient::TrackerClient(TorrentClient *downloader, QObject *parent)
+    : QObject(parent), torrentDownloader(downloader)
+{
+    length = 0;
+    requestInterval = 5 * 60;
+    requestIntervalTimer = -1;
+    firstTrackerRequest = true;
+    lastTrackerRequest = false;
+    firstSeeding = true;
+
+    connect(&http, SIGNAL(done(bool)), this, SLOT(httpRequestDone(bool)));
+}
+
+void TrackerClient::start(const MetaInfo &info)
+{
+    metaInfo = info;
+    QTimer::singleShot(0, this, SLOT(fetchPeerList()));
+
+    if (metaInfo.fileForm() == MetaInfo::SingleFileForm) {
+        length = metaInfo.singleFile().length;
+    } else {
+        QList<MetaInfoMultiFile> files = metaInfo.multiFiles();
+        for (int i = 0; i < files.size(); ++i)
+            length += files.at(i).length;
+    }
+}
+
+void TrackerClient::startSeeding()
+{
+    firstSeeding = true;
+    fetchPeerList();
+}
+
+void TrackerClient::stop()
+{
+    lastTrackerRequest = true;
+    http.abort();
+    fetchPeerList();
+}
+
+void TrackerClient::timerEvent(QTimerEvent *event)
+{
+    if (event->timerId() == requestIntervalTimer) {
+        if (http.state() == QHttp::Unconnected)
+            fetchPeerList();
+    } else {
+        QObject::timerEvent(event);
+    }
+}
+
+void TrackerClient::fetchPeerList()
+{
+    // Prepare connection details
+    QString fullUrl = metaInfo.announceUrl();
+    QUrl url(fullUrl);
+    QString passkey = "?";
+    if (fullUrl.contains("?passkey")) {
+        passkey = metaInfo.announceUrl().mid(fullUrl.indexOf("?passkey"), -1);
+        passkey += "&";
+    }
+
+    // Percent encode the hash
+    QByteArray infoHash = torrentDownloader->infoHash();
+    QString encodedSum;
+    for (int i = 0; i < infoHash.size(); ++i) {
+        encodedSum += '%';
+        encodedSum += QString::number(infoHash[i], 16).right(2).rightJustified(2, '0');
+    }
+
+    bool seeding = (torrentDownloader->state() == TorrentClient::Seeding);
+    QByteArray query;
+    query += url.path().toLatin1();
+    query += passkey;
+    query += "info_hash=" + encodedSum;
+    query += "&peer_id=" + ConnectionManager::instance()->clientId();
+    query += "&port=" + QByteArray::number(TorrentServer::instance()->serverPort());
+    query += "&compact=1";
+    query += "&uploaded=" + QByteArray::number(torrentDownloader->uploadedBytes());
+
+    if (!firstSeeding) {
+        query += "&downloaded=0";
+        query += "&left=0";
+    } else {
+        query += "&downloaded=" + QByteArray::number(
+            torrentDownloader->downloadedBytes());
+        int left = qMax<int>(0, metaInfo.totalSize() - torrentDownloader->downloadedBytes());
+        query += "&left=" + QByteArray::number(seeding ? 0 : left);
+    }
+
+    if (seeding && firstSeeding) {
+        query += "&event=completed";
+        firstSeeding = false;
+    } else if (firstTrackerRequest) {
+        firstTrackerRequest = false;
+        query += "&event=started";
+    } else if(lastTrackerRequest) {
+        query += "&event=stopped";
+    }
+
+    if (!trackerId.isEmpty())
+        query += "&trackerid=" + trackerId;
+
+    http.setHost(url.host(), url.port() == -1 ? 80 : url.port());
+    if (!url.userName().isEmpty())
+        http.setUser(url.userName(), url.password());
+    http.get(query);
+}
+
+void TrackerClient::httpRequestDone(bool error)
+{
+    if (lastTrackerRequest) {
+        emit stopped();
+        return;
+    }
+
+    if (error) {
+        emit connectionError(http.error());
+        return;
+    }
+
+    QByteArray response = http.readAll();
+    http.abort();
+
+    BencodeParser parser;
+    if (!parser.parse(response)) {
+        qWarning("Error parsing bencode response from tracker: %s",
+                 qPrintable(parser.errorString()));
+        http.abort();
+        return;
+    }
+
+    QMap<QByteArray, QVariant> dict = parser.dictionary();
+
+    if (dict.contains("failure reason")) {
+        // no other items are present
+        emit failure(QString::fromUtf8(dict.value("failure reason").toByteArray()));
+        return;
+    }
+
+    if (dict.contains("warning message")) {
+        // continue processing
+        emit warning(QString::fromUtf8(dict.value("warning message").toByteArray()));
+    }
+
+    if (dict.contains("tracker id")) {
+        // store it
+        trackerId = dict.value("tracker id").toByteArray();
+    }
+
+    if (dict.contains("interval")) {
+        // Mandatory item
+        if (requestIntervalTimer != -1)
+            killTimer(requestIntervalTimer);
+        requestIntervalTimer = startTimer(dict.value("interval").toInt() * 1000);
+    }
+
+    if (dict.contains("peers")) {
+        // store it
+        peers.clear();
+        QVariant peerEntry = dict.value("peers");
+        if (peerEntry.type() == QVariant::List) {
+            QList<QVariant> peerTmp = peerEntry.toList();
+            for (int i = 0; i < peerTmp.size(); ++i) {
+                TorrentPeer tmp;
+                QMap<QByteArray, QVariant> peer = qVariantValue<QMap<QByteArray, QVariant> >(peerTmp.at(i));
+                tmp.id = QString::fromUtf8(peer.value("peer id").toByteArray());
+                tmp.address.setAddress(QString::fromUtf8(peer.value("ip").toByteArray()));
+                tmp.port = peer.value("port").toInt();
+                peers << tmp;
+            }
+        } else {
+            QByteArray peerTmp = peerEntry.toByteArray();
+            for (int i = 0; i < peerTmp.size(); i += 6) {
+                TorrentPeer tmp;
+                uchar *data = (uchar *)peerTmp.constData() + i;
+                tmp.port = (int(data[4]) << 8) + data[5];
+                uint ipAddress = 0;
+                ipAddress += uint(data[0]) << 24;
+                ipAddress += uint(data[1]) << 16;
+                ipAddress += uint(data[2]) << 8;
+                ipAddress += uint(data[3]);
+                tmp.address.setAddress(ipAddress);
+                peers << tmp;
+            }
+        }
+        emit peerListUpdated(peers);
+    }
+}