|  | Home · All Classes · Main Classes · Grouped Classes · Modules · Functions |

# trackerclient.cpp Example File
network/torrent/trackerclient.cpp

    /****************************************************************************
    **
    ** Copyright (C) 2004-2006 Trolltech ASA. All rights reserved.
    **
    ** This file is part of the example classes of the Qt Toolkit.
    **
    ** Licensees holding valid Qt Preview licenses may use this file in
    ** accordance with the Qt Preview License Agreement provided with the
    ** Software.
    **
    ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
    ** information about Qt Commercial License Agreements.
    **
    ** Contact info@trolltech.com if any conditions of this licensing are
    ** not clear to you.
    **
    ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    **
    ****************************************************************************/

    #include "bencodeparser.h"
    #include "connectionmanager.h"
    #include "torrentclient.h"
    #include "torrentserver.h"
    #include "trackerclient.h"

    #include <QtCore>

    quint32 qntoh(quint32 source)
    {
    #if Q_BYTE_ORDER == Q_BIG_ENDIAN
        return source;
    #else
        return 0
            | ((source & 0x000000ff) << 24)
            | ((source & 0x0000ff00) << 8)
            | ((source & 0x00ff0000) >> 8)
            | ((source & 0xff000000) >> 24);
    #endif
    }

    TrackerClient::TrackerClient(TorrentClient *downloader, QObject *parent)
        : QObject(parent), torrentDownloader(downloader)
    {
        length = 0;
        uploadedBytes = 0;
        downloadedBytes = 0;
        requestInterval = 5 * 60;
        requestIntervalTimer = -1;
        firstTrackerRequest = true;
        lastTrackerRequest = false;

        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::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
        QUrl url(metaInfo.announceUrl());

        QByteArray infoHash = torrentDownloader->infoHash();
        unsigned int *messageDigest = (unsigned int *)infoHash.constData();

        // Percent encode the hash
        QByteArray encodedSum;
        const char hex[] = "0123456789ABCDEF";
        unsigned char chars[4];

        for (int i = 0; i < 5; ++i) {
            unsigned int digest = qntoh(messageDigest[i]);
            chars[0] = (digest & 0xff000000) >> 24;
            chars[1] = (digest & 0x00ff0000) >> 16;
            chars[2] = (digest & 0x0000ff00) >> 8;
            chars[3] = (digest & 0x000000ff);

            for (int j = 0; j < 4; ++j) {
                unsigned char c = chars[j];
                if ((c >= '0' && c <= '9')
                    || (c >= 'a' && c <= 'z')
                    || (c >= 'A' && c <= 'Z'))
                    encodedSum += c;
                else switch (c) {
                case '$': case '-': case '_': case '.':
                case '+': case '!': case '*': case '\'':
                case '(': case ')':
                    encodedSum += c;
                    break;
                default: {
                    encodedSum += "%";
                    encodedSum += hex[(c & 0xf0) >> 4];
                    encodedSum += hex[c & 0xf];
                    break;
                }
                }
            }
        }

        bool seeding = (torrentDownloader->state() == TorrentClient::Seeding);
        QByteArray query;
        query += url.path().toLatin1();
        query += "?";
        query += "info_hash=" + encodedSum;
        query += "&peer_id=" + ConnectionManager::instance()->clientId();
        query += "&port=" + QByteArray::number(TorrentServer::instance()->serverPort());
        query += "&uploaded=" + QByteArray::number(uploadedBytes);
        query += "&downloaded=" + QByteArray::number(downloadedBytes);
        query += "&left="+ QByteArray::number(seeding ? 0 : qMax<int>(0, length - downloadedBytes));
        query += "&compact=1";
        if (seeding) {
            query += "&event=completed";
        } 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);
        }
    }