File indexing completed on 2025-02-16 04:37:35

0001 /*
0002     SPDX-FileCopyrightText: 2009 Joris Guisson <joris.guisson@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "magnetdownloader.h"
0008 #include <dht/dhtbase.h>
0009 #include <dht/dhtpeersource.h>
0010 #include <peer/peer.h>
0011 #include <peer/peermanager.h>
0012 #include <torrent/globals.h>
0013 #include <tracker/httptracker.h>
0014 #include <tracker/udptracker.h>
0015 
0016 #include <KIO/StoredTransferJob>
0017 
0018 #include "bcodec/bdecoder.h"
0019 #include "bcodec/bnode.h"
0020 #include "util/error.h"
0021 #include <boost/concept_check.hpp>
0022 
0023 namespace bt
0024 {
0025 MagnetDownloader::MagnetDownloader(const bt::MagnetLink &mlink, QObject *parent)
0026     : QObject(parent)
0027     , mlink(mlink)
0028     , pman(nullptr)
0029     , dht_ps(nullptr)
0030     , tor(mlink.infoHash())
0031     , found(false)
0032 {
0033     dht::DHTBase &dht_table = Globals::instance().getDHT();
0034     connect(&dht_table, &dht::DHTBase::started, this, &MagnetDownloader::dhtStarted);
0035     connect(&dht_table, &dht::DHTBase::stopped, this, &MagnetDownloader::dhtStopped);
0036 }
0037 
0038 MagnetDownloader::~MagnetDownloader()
0039 {
0040     if (running())
0041         stop();
0042 }
0043 
0044 void MagnetDownloader::start()
0045 {
0046     if (running())
0047         return;
0048 
0049     if (!mlink.torrent().isEmpty()) {
0050         KIO::StoredTransferJob *job = KIO::storedGet(QUrl(mlink.torrent()), KIO::NoReload, KIO::HideProgressInfo);
0051         connect(job, &KIO::StoredTransferJob::result, this, &MagnetDownloader::onTorrentDownloaded);
0052     }
0053 
0054     pman = new PeerManager(tor);
0055     connect(pman, &PeerManager::newPeer, this, &MagnetDownloader::onNewPeer);
0056 
0057     const QList<QUrl> trackers_list = mlink.trackers();
0058     for (const QUrl &url : trackers_list) {
0059         Tracker *tracker;
0060         if (url.scheme() == QLatin1String("udp"))
0061             tracker = new UDPTracker(url, this, tor.getPeerID(), 0);
0062         else
0063             tracker = new HTTPTracker(url, this, tor.getPeerID(), 0);
0064         trackers << tracker;
0065         connect(tracker, &Tracker::peersReady, pman, &PeerManager::peerSourceReady);
0066         tracker->start();
0067     }
0068 
0069     dht::DHTBase &dht_table = Globals::instance().getDHT();
0070     if (dht_table.isRunning()) {
0071         dht_ps = new dht::DHTPeerSource(dht_table, mlink.infoHash(), mlink.displayName());
0072         dht_ps->setRequestInterval(0); // Do not wait if the announce task finishes
0073         connect(dht_ps, &dht::DHTPeerSource::peersReady, pman, &PeerManager::peerSourceReady);
0074         dht_ps->start();
0075     }
0076 
0077     pman->start(false);
0078 }
0079 
0080 void MagnetDownloader::stop()
0081 {
0082     if (!running())
0083         return;
0084 
0085     for (Tracker *tracker : std::as_const(trackers)) {
0086         tracker->stop();
0087         delete tracker;
0088     }
0089     trackers.clear();
0090 
0091     if (dht_ps) {
0092         dht_ps->stop();
0093         delete dht_ps;
0094         dht_ps = nullptr;
0095     }
0096 
0097     pman->stop();
0098     delete pman;
0099     pman = nullptr;
0100 }
0101 
0102 void MagnetDownloader::update()
0103 {
0104     if (pman)
0105         pman->update();
0106 }
0107 
0108 bool MagnetDownloader::running() const
0109 {
0110     return pman != nullptr;
0111 }
0112 
0113 Uint32 MagnetDownloader::numPeers() const
0114 {
0115     return pman ? pman->getNumConnectedPeers() : 0;
0116 }
0117 
0118 void MagnetDownloader::onNewPeer(Peer *p)
0119 {
0120     if (!p->getStats().extension_protocol) {
0121         // If the peer doesn't support the extension protocol,
0122         // kill it
0123         p->kill();
0124     } else {
0125         connect(p, &Peer::metadataDownloaded, this, &MagnetDownloader::onMetadataDownloaded);
0126     }
0127 }
0128 
0129 Uint64 MagnetDownloader::bytesDownloaded() const
0130 {
0131     return 0;
0132 }
0133 
0134 Uint64 MagnetDownloader::bytesUploaded() const
0135 {
0136     return 0;
0137 }
0138 
0139 Uint64 MagnetDownloader::bytesLeft() const
0140 {
0141     return 0;
0142 }
0143 
0144 bool MagnetDownloader::isPartialSeed() const
0145 {
0146     return false;
0147 }
0148 
0149 const bt::SHA1Hash &MagnetDownloader::infoHash() const
0150 {
0151     return mlink.infoHash();
0152 }
0153 
0154 void MagnetDownloader::onTorrentDownloaded(KJob *job)
0155 {
0156     if (!job)
0157         return;
0158 
0159     KIO::StoredTransferJob *stj = qobject_cast<KIO::StoredTransferJob *>(job);
0160     if (job->error()) {
0161         Out(SYS_GEN | LOG_DEBUG) << "Failed to download " << stj->url() << ": " << stj->errorString() << endl;
0162         return;
0163     }
0164 
0165     QByteArray data = stj->data();
0166     try {
0167         Torrent tor;
0168         tor.load(data, false);
0169         const TrackerTier *tier = tor.getTrackerList();
0170         while (tier) {
0171             mlink.tracker_urls.append(tier->urls);
0172             tier = tier->next;
0173         }
0174         onMetadataDownloaded(tor.getMetaData());
0175     } catch (...) {
0176         Out(SYS_GEN | LOG_NOTICE) << "Invalid torrent file from " << mlink.torrent() << endl;
0177     }
0178 }
0179 
0180 void MagnetDownloader::onMetadataDownloaded(const QByteArray &data)
0181 {
0182     if (found)
0183         return;
0184 
0185     bt::SHA1Hash hash = bt::SHA1Hash::generate((const Uint8 *)data.data(), data.size());
0186     if (hash != mlink.infoHash()) {
0187         Out(SYS_GEN | LOG_NOTICE) << "Metadata downloaded, but hash check failed" << endl;
0188         return;
0189     }
0190 
0191     found = true;
0192     Out(SYS_GEN | LOG_IMPORTANT) << "Metadata downloaded" << endl;
0193     foundMetadata(this, data);
0194     QTimer::singleShot(0, this, &MagnetDownloader::stop);
0195 }
0196 
0197 void MagnetDownloader::dhtStarted()
0198 {
0199     if (running() && !dht_ps) {
0200         dht::DHTBase &dht_table = Globals::instance().getDHT();
0201         dht_ps = new dht::DHTPeerSource(dht_table, mlink.infoHash(), mlink.displayName());
0202         dht_ps->setRequestInterval(0); // Do not wait if the announce task finishes
0203         connect(dht_ps, &dht::DHTPeerSource::peersReady, pman, &PeerManager::peerSourceReady);
0204         dht_ps->start();
0205     }
0206 }
0207 
0208 void MagnetDownloader::dhtStopped()
0209 {
0210     if (running() && dht_ps) {
0211         dht_ps->stop();
0212         delete dht_ps;
0213         dht_ps = nullptr;
0214     }
0215 }
0216 
0217 }
0218 
0219 #include "moc_magnetdownloader.cpp"