File indexing completed on 2025-01-05 04:37:27
0001 /* 0002 SPDX-FileCopyrightText: 2009 Joris Guisson <joris.guisson@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 #include "trackermanager.h" 0007 #include <QFile> 0008 #include <QTextStream> 0009 #include <klocalizedstring.h> 0010 #include <peer/peermanager.h> 0011 #include <torrent/torrent.h> 0012 #include <torrent/torrentcontrol.h> 0013 #include <tracker/httptracker.h> 0014 #include <tracker/tracker.h> 0015 #include <tracker/udptracker.h> 0016 #include <util/log.h> 0017 0018 namespace bt 0019 { 0020 TrackerManager::TrackerManager(bt::TorrentControl *tor, PeerManager *pman) 0021 : tor(tor) 0022 , pman(pman) 0023 , curr(nullptr) 0024 , started(false) 0025 { 0026 trackers.setAutoDelete(true); 0027 no_save_custom_trackers = false; 0028 0029 const TrackerTier *t = tor->getTorrent().getTrackerList(); 0030 int tier = 1; 0031 while (t) { 0032 // add all standard trackers 0033 const QList<QUrl> &tr = t->urls; 0034 QList<QUrl>::const_iterator i = tr.begin(); 0035 while (i != tr.end()) { 0036 addTracker(*i, false, tier); 0037 ++i; 0038 } 0039 0040 tier++; 0041 t = t->next; 0042 } 0043 0044 // load custom trackers 0045 loadCustomURLs(); 0046 // Load status of each tracker 0047 loadTrackerStatus(); 0048 0049 if (tor->getStats().priv_torrent) 0050 switchTracker(selectTracker()); 0051 } 0052 0053 TrackerManager::~TrackerManager() 0054 { 0055 saveCustomURLs(); 0056 saveTrackerStatus(); 0057 } 0058 0059 TrackerInterface *TrackerManager::getCurrentTracker() const 0060 { 0061 return curr; 0062 } 0063 0064 bool TrackerManager::noTrackersReachable() const 0065 { 0066 if (tor->getStats().priv_torrent) { 0067 return curr ? curr->trackerStatus() == TRACKER_ERROR : false; 0068 } else { 0069 int enabled = 0; 0070 0071 // If all trackers have an ERROR status, and there is at least one 0072 // enabled, we must return true; 0073 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) { 0074 if (i->second->isEnabled()) { 0075 if (i->second->trackerStatus() != TRACKER_ERROR) 0076 return false; 0077 enabled++; 0078 } 0079 } 0080 0081 return enabled > 0; 0082 } 0083 } 0084 0085 void addTrackerStatusToInfo(const Tracker *tr, TrackersStatusInfo &info) 0086 { 0087 if (tr) { 0088 info.trackers_count++; 0089 0090 if (tr->trackerStatus() == TRACKER_ERROR) { 0091 info.errors++; 0092 if (tr->timeOut()) 0093 info.timeout_errors++; 0094 } 0095 if (tr->hasWarning()) 0096 info.warnings++; 0097 } 0098 } 0099 0100 TrackersStatusInfo TrackerManager::getTrackersStatusInfo() const 0101 { 0102 TrackersStatusInfo tsi; 0103 tsi.trackers_count = tsi.errors = tsi.timeout_errors = tsi.warnings = 0; 0104 0105 if (tor->getStats().priv_torrent) { 0106 addTrackerStatusToInfo(curr, tsi); 0107 return tsi; 0108 } 0109 0110 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) 0111 if (i->second->isEnabled()) 0112 addTrackerStatusToInfo(i->second, tsi); 0113 return tsi; 0114 } 0115 0116 void TrackerManager::setCurrentTracker(bt::TrackerInterface *t) 0117 { 0118 if (!tor->getStats().priv_torrent) 0119 return; 0120 0121 Tracker *trk = (Tracker *)t; 0122 if (!trk) 0123 return; 0124 0125 if (curr != trk) { 0126 if (curr) 0127 curr->stop(); 0128 switchTracker(trk); 0129 trk->start(); 0130 } 0131 } 0132 0133 void TrackerManager::setCurrentTracker(const QUrl &url) 0134 { 0135 Tracker *trk = trackers.find(url); 0136 if (trk) 0137 setCurrentTracker(trk); 0138 } 0139 0140 QList<TrackerInterface *> TrackerManager::getTrackers() 0141 { 0142 QList<TrackerInterface *> ret; 0143 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) { 0144 ret.append(i->second); 0145 } 0146 0147 return ret; 0148 } 0149 0150 TrackerInterface *TrackerManager::addTracker(const QUrl &url, bool custom, int tier) 0151 { 0152 if (trackers.contains(url)) 0153 return nullptr; 0154 0155 Tracker *trk = nullptr; 0156 if (url.scheme() == QLatin1String("udp")) 0157 trk = new UDPTracker(url, this, tor->getTorrent().getPeerID(), tier); 0158 else if (url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https")) 0159 trk = new HTTPTracker(url, this, tor->getTorrent().getPeerID(), tier); 0160 else 0161 return nullptr; 0162 0163 addTracker(trk); 0164 if (custom) { 0165 custom_trackers.append(url); 0166 if (!no_save_custom_trackers) { 0167 saveCustomURLs(); 0168 saveTrackerStatus(); 0169 } 0170 } 0171 0172 return trk; 0173 } 0174 0175 bool TrackerManager::removeTracker(bt::TrackerInterface *t) 0176 { 0177 return removeTracker(t->trackerURL()); 0178 } 0179 0180 bool TrackerManager::removeTracker(const QUrl &url) 0181 { 0182 if (!custom_trackers.contains(url)) 0183 return false; 0184 0185 custom_trackers.removeAll(url); 0186 Tracker *trk = trackers.find(url); 0187 if (trk && curr == trk && tor->getStats().priv_torrent) { 0188 // do a timed delete on the tracker, so the stop signal 0189 // has plenty of time to reach it 0190 trk->stop(); 0191 trk->timedDelete(10 * 1000); 0192 trackers.setAutoDelete(false); 0193 trackers.erase(url); 0194 trackers.setAutoDelete(true); 0195 0196 if (trackers.count() > 0) { 0197 switchTracker(selectTracker()); 0198 if (curr) 0199 curr->start(); 0200 } 0201 } else { 0202 // just delete if not the current one 0203 trackers.erase(url); 0204 } 0205 saveCustomURLs(); 0206 return true; 0207 } 0208 0209 bool TrackerManager::canRemoveTracker(bt::TrackerInterface *t) 0210 { 0211 return custom_trackers.contains(t->trackerURL()); 0212 } 0213 0214 void TrackerManager::restoreDefault() 0215 { 0216 QList<QUrl>::iterator i = custom_trackers.begin(); 0217 while (i != custom_trackers.end()) { 0218 Tracker *t = trackers.find(*i); 0219 if (t) { 0220 if (t->isStarted()) 0221 t->stop(); 0222 0223 if (curr == t && tor->getStats().priv_torrent) { 0224 curr = nullptr; 0225 trackers.erase(*i); 0226 } else { 0227 trackers.erase(*i); 0228 } 0229 } 0230 ++i; 0231 } 0232 0233 custom_trackers.clear(); 0234 saveCustomURLs(); 0235 if (tor->getStats().priv_torrent && curr == nullptr) 0236 switchTracker(selectTracker()); 0237 } 0238 0239 void TrackerManager::addTracker(Tracker *trk) 0240 { 0241 trackers.insert(trk->trackerURL(), trk); 0242 connect(trk, &Tracker::peersReady, pman, &PeerManager::peerSourceReady); 0243 connect(trk, &Tracker::scrapeDone, tor, &TorrentControl::trackerScrapeDone); 0244 connect(trk, &Tracker::requestOK, this, &TrackerManager::onTrackerOK); 0245 connect(trk, &Tracker::requestFailed, this, &TrackerManager::onTrackerError); 0246 } 0247 0248 void TrackerManager::start() 0249 { 0250 if (started) 0251 return; 0252 0253 if (tor->getStats().priv_torrent) { 0254 if (!curr) { 0255 if (trackers.count() > 0) { 0256 switchTracker(selectTracker()); 0257 if (curr) 0258 curr->start(); 0259 } 0260 } else { 0261 curr->start(); 0262 } 0263 } else { 0264 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) { 0265 if (i->second->isEnabled()) 0266 i->second->start(); 0267 } 0268 } 0269 0270 started = true; 0271 } 0272 0273 void TrackerManager::stop(bt::WaitJob *wjob) 0274 { 0275 if (!started) 0276 return; 0277 0278 started = false; 0279 if (tor->getStats().priv_torrent) { 0280 if (curr) 0281 curr->stop(wjob); 0282 0283 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) { 0284 i->second->reset(); 0285 } 0286 } else { 0287 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) { 0288 i->second->stop(wjob); 0289 i->second->reset(); 0290 } 0291 } 0292 } 0293 0294 void TrackerManager::completed() 0295 { 0296 if (tor->getStats().priv_torrent) { 0297 if (curr) 0298 curr->completed(); 0299 } else { 0300 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) { 0301 i->second->completed(); 0302 } 0303 } 0304 } 0305 0306 void TrackerManager::scrape() 0307 { 0308 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) { 0309 i->second->scrape(); 0310 } 0311 } 0312 0313 void TrackerManager::manualUpdate() 0314 { 0315 if (tor->getStats().priv_torrent) { 0316 if (curr) { 0317 curr->manualUpdate(); 0318 } 0319 } else { 0320 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) { 0321 if (i->second->isEnabled()) 0322 i->second->manualUpdate(); 0323 } 0324 } 0325 } 0326 0327 void TrackerManager::saveCustomURLs() 0328 { 0329 QString trackers_file = tor->getTorDir() + QLatin1String("trackers"); 0330 QFile file(trackers_file); 0331 if (!file.open(QIODevice::WriteOnly)) 0332 return; 0333 0334 QTextStream stream(&file); 0335 for (const QUrl &url : std::as_const(custom_trackers)) 0336 stream << url.toDisplayString() << Qt::endl; 0337 } 0338 0339 void TrackerManager::loadCustomURLs() 0340 { 0341 QString trackers_file = tor->getTorDir() + QLatin1String("trackers"); 0342 QFile file(trackers_file); 0343 if (!file.open(QIODevice::ReadOnly)) 0344 return; 0345 0346 no_save_custom_trackers = true; 0347 QTextStream stream(&file); 0348 while (!stream.atEnd()) { 0349 addTracker(QUrl(stream.readLine()), true); 0350 } 0351 no_save_custom_trackers = false; 0352 } 0353 0354 void TrackerManager::saveTrackerStatus() 0355 { 0356 QString status_file = tor->getTorDir() + QLatin1String("tracker_status"); 0357 QFile file(status_file); 0358 if (!file.open(QIODevice::WriteOnly)) 0359 return; 0360 0361 QTextStream stream(&file); 0362 PtrMap<QUrl, Tracker>::iterator i = trackers.begin(); 0363 while (i != trackers.end()) { 0364 QUrl url = i->first; 0365 Tracker *trk = i->second; 0366 0367 stream << (trk->isEnabled() ? "1:" : "0:") << url.toDisplayString() << Qt::endl; 0368 ++i; 0369 } 0370 } 0371 0372 void TrackerManager::loadTrackerStatus() 0373 { 0374 QString status_file = tor->getTorDir() + QLatin1String("tracker_status"); 0375 QFile file(status_file); 0376 if (!file.open(QIODevice::ReadOnly)) 0377 return; 0378 0379 QTextStream stream(&file); 0380 while (!stream.atEnd()) { 0381 QString line = stream.readLine(); 0382 if (line.size() < 2) 0383 continue; 0384 0385 if (line[0] == '0') { 0386 Tracker *trk = trackers.find(QUrl(line.mid(2))); // url starts at the second char 0387 if (trk) 0388 trk->setEnabled(false); 0389 } 0390 } 0391 } 0392 0393 Tracker *TrackerManager::selectTracker() 0394 { 0395 Tracker *n = nullptr; 0396 PtrMap<QUrl, Tracker>::iterator i = trackers.begin(); 0397 while (i != trackers.end()) { 0398 Tracker *t = i->second; 0399 if (t->isEnabled()) { 0400 if (!n) 0401 n = t; 0402 else if (t->failureCount() < n->failureCount()) 0403 n = t; 0404 else if (t->failureCount() == n->failureCount()) 0405 n = t->getTier() < n->getTier() ? t : n; 0406 } 0407 ++i; 0408 } 0409 0410 if (n) { 0411 Out(SYS_TRK | LOG_DEBUG) << "Selected tracker " << n->trackerURL().toString() << " (tier = " << n->getTier() << ")" << endl; 0412 } 0413 0414 return n; 0415 } 0416 0417 void TrackerManager::onTrackerError(const QString &err) 0418 { 0419 Q_UNUSED(err); 0420 if (!started) 0421 return; 0422 0423 if (!tor->getStats().priv_torrent) { 0424 Tracker *trk = (Tracker *)sender(); 0425 trk->handleFailure(); 0426 } else { 0427 Tracker *trk = (Tracker *)sender(); 0428 if (trk == curr) { 0429 // select an other tracker 0430 trk = selectTracker(); 0431 if (trk == curr) { // if we can't find another handle the failure 0432 trk->handleFailure(); 0433 } else { 0434 curr->stop(); 0435 switchTracker(trk); 0436 if (curr->failureCount() > 0) 0437 curr->handleFailure(); 0438 else 0439 curr->start(); 0440 } 0441 } else 0442 trk->handleFailure(); 0443 } 0444 } 0445 0446 void TrackerManager::onTrackerOK() 0447 { 0448 Tracker *tracker = (Tracker *)sender(); 0449 if (tracker->isStarted()) 0450 tracker->scrape(); 0451 } 0452 0453 void TrackerManager::updateCurrentManually() 0454 { 0455 if (!curr) 0456 return; 0457 0458 curr->manualUpdate(); 0459 } 0460 0461 void TrackerManager::switchTracker(Tracker *trk) 0462 { 0463 if (curr == trk) 0464 return; 0465 0466 curr = trk; 0467 if (curr) 0468 Out(SYS_TRK | LOG_NOTICE) << "Switching to tracker " << trk->trackerURL() << endl; 0469 } 0470 0471 Uint32 TrackerManager::getNumSeeders() const 0472 { 0473 if (tor->getStats().priv_torrent) { 0474 return curr && curr->getNumSeeders() > 0 ? curr->getNumSeeders() : 0; 0475 } 0476 0477 int r = 0; 0478 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) { 0479 int v = i->second->getNumSeeders(); 0480 if (v > r) 0481 r = v; 0482 } 0483 0484 return r; 0485 } 0486 0487 Uint32 TrackerManager::getNumLeechers() const 0488 { 0489 if (tor->getStats().priv_torrent) 0490 return curr && curr->getNumLeechers() > 0 ? curr->getNumLeechers() : 0; 0491 0492 int r = 0; 0493 for (PtrMap<QUrl, Tracker>::const_iterator i = trackers.begin(); i != trackers.end(); ++i) { 0494 int v = i->second->getNumLeechers(); 0495 if (v > r) 0496 r = v; 0497 } 0498 0499 return r; 0500 } 0501 0502 void TrackerManager::setTrackerEnabled(const QUrl &url, bool enabled) 0503 { 0504 Tracker *trk = trackers.find(url); 0505 if (!trk) 0506 return; 0507 0508 trk->setEnabled(enabled); 0509 if (!enabled) { 0510 trk->stop(); 0511 if (curr == trk) { // if the current tracker is disabled, switch to another one 0512 switchTracker(selectTracker()); 0513 if (curr) 0514 curr->start(); 0515 } 0516 } else { 0517 // start tracker if necessary 0518 if (!tor->getStats().priv_torrent && started) 0519 trk->start(); 0520 } 0521 0522 saveTrackerStatus(); 0523 } 0524 0525 Uint64 TrackerManager::bytesDownloaded() const 0526 { 0527 const TorrentStats &s = tor->getStats(); 0528 if (s.imported_bytes > s.bytes_downloaded) 0529 return 0; 0530 else 0531 return s.bytes_downloaded - s.imported_bytes; 0532 } 0533 0534 Uint64 TrackerManager::bytesUploaded() const 0535 { 0536 return tor->getStats().bytes_uploaded; 0537 } 0538 0539 Uint64 TrackerManager::bytesLeft() const 0540 { 0541 return tor->getStats().bytes_left; 0542 } 0543 0544 const bt::SHA1Hash &TrackerManager::infoHash() const 0545 { 0546 return tor->getInfoHash(); 0547 } 0548 0549 bool TrackerManager::isPartialSeed() const 0550 { 0551 return pman->isPartialSeed(); 0552 } 0553 0554 } 0555 0556 #include "moc_trackermanager.cpp"