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"