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"