File indexing completed on 2025-01-05 04:37:25

0001 /*
0002     SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com>
0003     SPDX-FileCopyrightText: 2005 Ivan Vasic <ivasic@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 #include "torrentcontrol.h"
0008 
0009 #include <QDateTime>
0010 #include <QDir>
0011 #include <QFile>
0012 #include <QTextCodec>
0013 #include <QTextStream>
0014 
0015 #include <KLocalizedString>
0016 
0017 #include "choker.h"
0018 #include "globals.h"
0019 #include "jobqueue.h"
0020 #include "peersourcemanager.h"
0021 #include "server.h"
0022 #include "statsfile.h"
0023 #include "timeestimator.h"
0024 #include "torrent.h"
0025 #include "torrentfile.h"
0026 #include "torrentfilestream.h"
0027 #include "uploader.h"
0028 #include <datachecker/datacheckerjob.h>
0029 #include <datachecker/datacheckerthread.h>
0030 #include <datachecker/multidatachecker.h>
0031 #include <datachecker/singledatachecker.h>
0032 #include <dht/dhtbase.h>
0033 #include <diskio/cache.h>
0034 #include <diskio/chunkmanager.h>
0035 #include <diskio/movedatafilesjob.h>
0036 #include <diskio/preallocationjob.h>
0037 #include <diskio/preallocationthread.h>
0038 #include <download/downloader.h>
0039 #include <download/webseed.h>
0040 #include <interfaces/cachefactory.h>
0041 #include <interfaces/chunkselectorinterface.h>
0042 #include <interfaces/monitorinterface.h>
0043 #include <interfaces/queuemanagerinterface.h>
0044 #include <interfaces/trackerslist.h>
0045 #include <kio/copyjob.h>
0046 #include <net/socketmonitor.h>
0047 #include <peer/peer.h>
0048 #include <peer/peerdownloader.h>
0049 #include <peer/peermanager.h>
0050 #include <util/bitset.h>
0051 #include <util/error.h>
0052 #include <util/fileops.h>
0053 #include <util/functions.h>
0054 #include <util/log.h>
0055 #include <util/waitjob.h>
0056 
0057 namespace bt
0058 {
0059 bool TorrentControl::completed_datacheck = false;
0060 Uint32 TorrentControl::min_diskspace = 100;
0061 
0062 TorrentControl::TorrentControl()
0063     : m_qman(nullptr)
0064     , tor(nullptr)
0065     , psman(nullptr)
0066     , cman(nullptr)
0067     , pman(nullptr)
0068     , downloader(nullptr)
0069     , uploader(nullptr)
0070     , choke(nullptr)
0071     , tmon(nullptr)
0072     , prealloc(false)
0073 {
0074     job_queue = new JobQueue(this);
0075     cache_factory = nullptr;
0076     istats.session_bytes_uploaded = 0;
0077     old_tordir = QString();
0078     stats_file = nullptr;
0079     loading_stats = false;
0080 
0081     istats.running_time_dl = istats.running_time_ul = 0;
0082     istats.prev_bytes_dl = 0;
0083     istats.prev_bytes_ul = 0;
0084     istats.io_error = false;
0085     istats.priority = 0;
0086     istats.custom_output_name = false;
0087     istats.diskspace_warning_emitted = false;
0088     istats.dht_on = false;
0089     updateStats();
0090 
0091     m_eta = new TimeEstimator(this);
0092     // by default no torrent limits
0093     upload_gid = download_gid = 0;
0094     upload_limit = download_limit = 0;
0095     assured_upload_speed = assured_download_speed = 0;
0096     last_diskspace_check = bt::CurrentTime();
0097 }
0098 
0099 TorrentControl::~TorrentControl()
0100 {
0101     if (stats.running) {
0102         // block all signals to prevent crash at exit
0103         blockSignals(true);
0104         stop(nullptr);
0105     }
0106 
0107     if (tmon)
0108         tmon->destroyed();
0109 
0110     if (downloader)
0111         downloader->saveWebSeeds(tordir + "webseeds");
0112 
0113     delete job_queue;
0114     delete choke;
0115     delete downloader;
0116     delete uploader;
0117     delete cman;
0118     delete pman;
0119     delete psman;
0120     delete tor;
0121     delete m_eta;
0122     delete cache_factory;
0123     delete stats_file;
0124 }
0125 
0126 void TorrentControl::update()
0127 {
0128     UpdateCurrentTime();
0129 
0130     if (istats.io_error) {
0131         stop();
0132         Q_EMIT stoppedByError(this, stats.error_msg);
0133         return;
0134     }
0135 
0136     if (stats.paused) {
0137         stalled_timer.update();
0138         pman->update();
0139         updateStatus();
0140         updateStats();
0141         return;
0142     }
0143 
0144     if (prealloc && preallocate())
0145         return;
0146 
0147     try {
0148         // first update peermanager
0149         pman->update();
0150         bool comp = stats.completed;
0151 
0152         // then the downloader and uploader
0153         uploader->update();
0154         downloader->update();
0155 
0156         // helper var, check if needed to move completed files somewhere
0157         bool moveCompleted = false;
0158         bool checkOnCompletion = false;
0159 
0160         stats.completed = cman->completed();
0161         if (stats.completed && !comp) {
0162             pman->killSeeders();
0163             QDateTime now = QDateTime::currentDateTime();
0164             istats.running_time_dl += istats.time_started_dl.secsTo(now);
0165             updateStatus();
0166             updateStats();
0167 
0168             // download has just been completed
0169             // only sent completed to tracker when we have all chunks (so no excluded chunks)
0170             if (cman->haveAllChunks())
0171                 psman->completed();
0172             pman->setPartialSeed(!cman->haveAllChunks() && cman->chunksLeft() == 0);
0173 
0174             finished(this);
0175 
0176             // Move completed download to specified directory if needed
0177             moveCompleted = !completed_dir.isEmpty();
0178 
0179             // See if we need to do a data check
0180             if (completed_datacheck)
0181                 checkOnCompletion = true;
0182         } else if (!stats.completed && comp) {
0183             // restart download if necesarry
0184             // when user selects that files which were previously excluded,
0185             // should now be downloaded
0186             if (!psman->isStarted())
0187                 psman->start();
0188             else
0189                 psman->manualUpdate();
0190             istats.time_started_dl = QDateTime::currentDateTime();
0191             // Tell QM to redo queue
0192             updateQueue();
0193             if (stats.superseeding)
0194                 pman->setSuperSeeding(false, cman->getBitSet());
0195         }
0196         updateStatus();
0197 
0198         if (wanted_update_timer.getElapsedSinceUpdate() >= 60 * 1000) {
0199             // Get a list of the chunks I have...
0200             BitSet wanted_chunks = cman->getBitSet();
0201             // or don't want...
0202             wanted_chunks.orBitSet(cman->getExcludedBitSet());
0203             wanted_chunks.orBitSet(cman->getOnlySeedBitSet());
0204             // and inverted it get a list of the chunks I want
0205             wanted_chunks.invert();
0206             pman->setWantedChunks(wanted_chunks);
0207             wanted_update_timer.update();
0208         }
0209 
0210         // we may need to update the choker
0211         if (choker_update_timer.getElapsedSinceUpdate() >= 10000 || pman->chokerNeedsToRun()) {
0212             // also get rid of seeders & uninterested when download is finished
0213             // no need to keep them around, but also no need to do this
0214             // every update, so once every 10 seconds is fine
0215             if (stats.completed) {
0216                 pman->killSeeders();
0217             }
0218 
0219             doChoking();
0220             choker_update_timer.update();
0221             // a good opportunity to make sure we are not keeping to much in memory
0222             cman->checkMemoryUsage();
0223         }
0224 
0225         // to satisfy people obsessed with their share ratio
0226         bool save_stats = m_qman ? m_qman->permitStatsSync(this) : (stats_save_timer.getElapsedSinceUpdate() >= 5 * 60 * 1000);
0227 
0228         if (save_stats) {
0229             saveStats();
0230             stats_save_timer.update();
0231         }
0232 
0233         // Update DownloadCap
0234         updateStats();
0235 
0236         if (stats.download_rate > 100) {
0237             stalled_timer.update();
0238             stats.last_download_activity_time = CurrentTime();
0239         }
0240 
0241         if (stats.upload_rate > 100)
0242             stats.last_upload_activity_time = CurrentTime();
0243 
0244         // do a manual update if we are stalled for more then 2 minutes
0245         // we do not do this for private torrents
0246         if (stalled_timer.getElapsedSinceUpdate() > 120000 && !stats.completed && !stats.priv_torrent) {
0247             Out(SYS_TRK | LOG_NOTICE) << "Stalled for too long, time to get some fresh blood" << endl;
0248             psman->manualUpdate();
0249             stalled_timer.update();
0250         }
0251 
0252         if (stats.completed && (overMaxRatio() || overMaxSeedTime())) {
0253             stats.auto_stopped = true;
0254             stop();
0255             Q_EMIT seedingAutoStopped(this, overMaxRatio() ? MAX_RATIO_REACHED : MAX_SEED_TIME_REACHED);
0256         }
0257 
0258         // Update diskspace if needed (every 1 min)
0259         if (!stats.completed && stats.running && bt::CurrentTime() - last_diskspace_check >= 60 * 1000) {
0260             checkDiskSpace(true);
0261         }
0262 
0263         // Emit the needDataCheck signal if needed
0264         if (checkOnCompletion)
0265             Q_EMIT needDataCheck(this);
0266 
0267         // Move completed files if needed:
0268         if (moveCompleted)
0269             moveToCompletedDir();
0270     }
0271 #ifndef Q_WS_WIN
0272     catch (BusError &e) {
0273         Out(SYS_DIO | LOG_IMPORTANT) << "Caught SIGBUS " << endl;
0274         if (!e.write_operation)
0275             onIOError(e.toString());
0276         else
0277             onIOError(i18n("Error writing to disk, do you have enough diskspace?"));
0278     }
0279 #endif
0280     catch (Error &e) {
0281         onIOError(e.toString());
0282     }
0283 }
0284 
0285 void TorrentControl::onIOError(const QString &msg)
0286 {
0287     Out(SYS_DIO | LOG_IMPORTANT) << "Error : " << msg << endl;
0288     stats.stopped_by_error = true;
0289     stats.status = ERROR;
0290     stats.error_msg = msg;
0291     istats.io_error = true;
0292     statusChanged(this);
0293 }
0294 
0295 void TorrentControl::pause()
0296 {
0297     if (!stats.running || stats.paused)
0298         return;
0299 
0300     pman->pause();
0301 
0302     try {
0303         downloader->saveDownloads(tordir + "current_chunks");
0304     } catch (Error &e) {
0305         // print out warning in case of failure
0306         // it doesn't corrupt the data, so just a couple of lost chunks
0307         Out(SYS_GEN | LOG_NOTICE) << "Warning : " << e.toString() << endl;
0308     }
0309 
0310     downloader->pause();
0311     downloader->saveWebSeeds(tordir + "webseeds");
0312     downloader->removeAllWebSeeds();
0313     cman->stop();
0314     stats.paused = true;
0315     updateRunningTimes();
0316     saveStats();
0317     statusChanged(this);
0318 
0319     Out(SYS_GEN | LOG_NOTICE) << "Paused " << tor->getNameSuggestion() << endl;
0320 }
0321 
0322 void TorrentControl::unpause()
0323 {
0324     if (!stats.running || !stats.paused || job_queue->runningJobs())
0325         return;
0326 
0327     cman->start();
0328 
0329     try {
0330         downloader->loadDownloads(tordir + "current_chunks");
0331     } catch (Error &e) {
0332         // print out warning in case of failure
0333         // we can still continue the download
0334         Out(SYS_GEN | LOG_NOTICE) << "Warning : " << e.toString() << endl;
0335     }
0336 
0337     downloader->loadWebSeeds(tordir + "webseeds");
0338     pman->unpause();
0339     loadStats();
0340     istats.time_started_ul = istats.time_started_dl = QDateTime::currentDateTime();
0341     stats.paused = false;
0342     statusChanged(this);
0343     Out(SYS_GEN | LOG_NOTICE) << "Unpaused " << tor->getNameSuggestion() << endl;
0344 }
0345 
0346 void TorrentControl::start()
0347 {
0348     // do not start running torrents or when there is a job running
0349     if (stats.running || job_queue->runningJobs())
0350         return;
0351 
0352     if (stats.running && stats.paused) {
0353         unpause();
0354         return;
0355     }
0356 
0357     stats.paused = false;
0358     stats.stopped_by_error = false;
0359     istats.io_error = false;
0360     istats.diskspace_warning_emitted = false;
0361     try {
0362         bool ret = true;
0363         aboutToBeStarted(this, ret);
0364         if (!ret)
0365             return;
0366     } catch (Error &err) {
0367         // something went wrong when files were recreated, set error and rethrow
0368         onIOError(err.toString());
0369         return;
0370     }
0371 
0372     try {
0373         cman->start();
0374     } catch (Error &e) {
0375         onIOError(e.toString());
0376         throw;
0377     }
0378 
0379     istats.time_started_ul = istats.time_started_dl = QDateTime::currentDateTime();
0380 
0381     if (prealloc) {
0382         if (preallocate())
0383             return;
0384     }
0385 
0386     continueStart();
0387 }
0388 
0389 void TorrentControl::continueStart()
0390 {
0391     // continues start after the prealloc_thread has finished preallocation
0392     pman->start(stats.completed && stats.superseeding);
0393     pman->loadPeerList(tordir + "peer_list");
0394     try {
0395         downloader->loadDownloads(tordir + "current_chunks");
0396     } catch (Error &e) {
0397         // print out warning in case of failure
0398         // we can still continue the download
0399         Out(SYS_GEN | LOG_NOTICE) << "Warning : " << e.toString() << endl;
0400     }
0401 
0402     loadStats();
0403     stats.running = true;
0404     stats.started = true;
0405     stats.queued = false;
0406     stats.last_download_activity_time = stats.last_upload_activity_time = CurrentTime();
0407     choker_update_timer.update();
0408     stats_save_timer.update();
0409     wanted_update_timer.update();
0410 
0411     stalled_timer.update();
0412     psman->start();
0413     stalled_timer.update();
0414     pman->setPartialSeed(!cman->haveAllChunks() && cman->chunksLeft() == 0);
0415 }
0416 
0417 void TorrentControl::updateRunningTimes()
0418 {
0419     QDateTime now = QDateTime::currentDateTime();
0420     if (!stats.completed)
0421         istats.running_time_dl += istats.time_started_dl.secsTo(now);
0422     istats.running_time_ul += istats.time_started_ul.secsTo(now);
0423     istats.time_started_ul = istats.time_started_dl = now;
0424 }
0425 
0426 void TorrentControl::stop(WaitJob *wjob)
0427 {
0428     if (!stats.paused)
0429         updateRunningTimes();
0430 
0431     // stop preallocation
0432     if (job_queue->currentJob() && job_queue->currentJob()->torrentStatus() == ALLOCATING_DISKSPACE)
0433         job_queue->currentJob()->kill(false);
0434 
0435     if (stats.running) {
0436         psman->stop(wjob);
0437 
0438         if (tmon)
0439             tmon->stopped();
0440 
0441         try {
0442             downloader->saveDownloads(tordir + "current_chunks");
0443         } catch (Error &e) {
0444             // print out warning in case of failure
0445             // it doesn't corrupt the data, so just a couple of lost chunks
0446             Out(SYS_GEN | LOG_NOTICE) << "Warning : " << e.toString() << endl;
0447         }
0448 
0449         downloader->clearDownloads();
0450     }
0451 
0452     pman->savePeerList(tordir + "peer_list");
0453     pman->stop();
0454     cman->stop();
0455 
0456     stats.running = false;
0457     stats.autostart = wjob != nullptr;
0458     stats.queued = false;
0459     stats.paused = false;
0460     saveStats();
0461     updateStatus();
0462     updateStats();
0463 
0464     Q_EMIT torrentStopped(this);
0465 }
0466 
0467 void TorrentControl::setMonitor(MonitorInterface *tmo)
0468 {
0469     tmon = tmo;
0470     downloader->setMonitor(tmon);
0471     if (tmon) {
0472         const QList<Peer::Ptr> ppl = pman->getPeers();
0473         for (const Peer::Ptr &peer : ppl)
0474             tmon->peerAdded(peer.data());
0475     }
0476 
0477     tor->setMonitor(tmon);
0478 }
0479 
0480 void TorrentControl::init(QueueManagerInterface *qman, const QByteArray &data, const QString &tmpdir, const QString &ddir)
0481 {
0482     m_qman = qman;
0483 
0484     // first load the torrent file
0485     tor = new Torrent();
0486     try {
0487         tor->load(data, false);
0488     } catch (bt::Error &err) {
0489         Out(SYS_GEN | LOG_NOTICE) << "Failed to load torrent: " << err.toString() << endl;
0490         delete tor;
0491         tor = nullptr;
0492         throw Error(i18n("An error occurred while loading <b>%1</b>:<br/><b>%2</b>", loadUrl().toString(), err.toString()));
0493     }
0494 
0495     tor->setFilePriorityListener(this);
0496     initInternal(qman, tmpdir, ddir);
0497 
0498     // copy data into torrent file
0499     QString tor_copy = tordir + "torrent";
0500     QFile fptr(tor_copy);
0501     if (!fptr.open(QIODevice::WriteOnly))
0502         throw Error(i18n("Unable to create %1: %2", tor_copy, fptr.errorString()));
0503 
0504     fptr.write(data.data(), data.size());
0505 }
0506 
0507 void TorrentControl::checkExisting(QueueManagerInterface *qman)
0508 {
0509     // check if we haven't already loaded the torrent
0510     // only do this when qman isn't 0
0511     if (qman && qman->alreadyLoaded(tor->getInfoHash())) {
0512         if (!stats.priv_torrent) {
0513             qman->mergeAnnounceList(tor->getInfoHash(), tor->getTrackerList());
0514             throw Warning(
0515                 i18n("You are already downloading the torrent <b>%1</b>. "
0516                      "The tracker lists from both torrents have been merged.",
0517                      tor->getNameSuggestion()));
0518         } else {
0519             throw Warning(i18n("You are already downloading the torrent <b>%1</b>.", tor->getNameSuggestion()));
0520         }
0521     }
0522 }
0523 
0524 void TorrentControl::setupDirs(const QString &tmpdir, const QString &ddir)
0525 {
0526     tordir = tmpdir;
0527 
0528     if (!tordir.endsWith(DirSeparator()))
0529         tordir += DirSeparator();
0530 
0531     outputdir = ddir.trimmed();
0532     if (outputdir.length() > 0 && !outputdir.endsWith(DirSeparator()))
0533         outputdir += DirSeparator();
0534 
0535     if (!bt::Exists(tordir)) {
0536         bt::MakeDir(tordir);
0537     }
0538 }
0539 
0540 void TorrentControl::setupStats()
0541 {
0542     stats.completed = false;
0543     stats.running = false;
0544     stats.torrent_name = tor->getNameSuggestion();
0545     stats.multi_file_torrent = tor->isMultiFile();
0546     stats.total_bytes = tor->getTotalSize();
0547     stats.priv_torrent = tor->isPrivate();
0548 
0549     // check the stats file for the custom_output_name variable
0550     if (!stats_file)
0551         stats_file = new StatsFile(tordir + "stats");
0552 
0553     if (stats_file->hasKey("CUSTOM_OUTPUT_NAME") && stats_file->readULong("CUSTOM_OUTPUT_NAME") == 1) {
0554         istats.custom_output_name = true;
0555     }
0556 
0557     if (stats.time_added.isNull())
0558         stats.time_added = QDateTime::currentDateTime();
0559 
0560     // load outputdir if outputdir is null
0561     if (outputdir.isNull() || outputdir.length() == 0)
0562         loadOutputDir();
0563 }
0564 
0565 void TorrentControl::setupData()
0566 {
0567     // create PeerManager and Tracker
0568     pman = new PeerManager(*tor);
0569     // Out() << "Tracker url " << url << " " << url.protocol() << " " << url.prettyURL() << endl;
0570     psman = new PeerSourceManager(this, pman);
0571 
0572     // Create chunkmanager, load the index file if it exists
0573     // else create all the necesarry files
0574     cman = new ChunkManager(*tor, tordir, outputdir, istats.custom_output_name, cache_factory);
0575     if (bt::Exists(tordir + "index"))
0576         cman->loadIndexFile();
0577 
0578     connect(cman, &ChunkManager::updateStats, this, &TorrentControl::updateStats);
0579     updateStats();
0580     stats.completed = cman->completed();
0581 
0582     // create downloader, uploader and choker
0583     downloader = new Downloader(*tor, *pman, *cman);
0584     downloader->loadWebSeeds(tordir + "webseeds");
0585     connect(downloader, &Downloader::ioError, this, &TorrentControl::onIOError);
0586     connect(downloader, &Downloader::chunkDownloaded, this, &TorrentControl::downloaded);
0587     uploader = new Uploader(*cman, *pman);
0588     choke = new Choker(*pman, *cman);
0589 
0590     connect(pman, &PeerManager::newPeer, this, &TorrentControl::onNewPeer);
0591     connect(pman, &PeerManager::peerKilled, this, &TorrentControl::onPeerRemoved);
0592     connect(cman, &ChunkManager::excluded, downloader, &Downloader::onExcluded);
0593     connect(cman, &ChunkManager::included, downloader, &Downloader::onIncluded);
0594     connect(cman, &ChunkManager::corrupted, this, &TorrentControl::corrupted);
0595 }
0596 
0597 void TorrentControl::initInternal(QueueManagerInterface *qman, const QString &tmpdir, const QString &ddir)
0598 {
0599     checkExisting(qman);
0600     setupDirs(tmpdir, ddir);
0601     setupStats();
0602     loadEncoding();
0603     setupData();
0604     updateStatus();
0605 
0606     // to get rid of phantom bytes we need to take into account
0607     // the data from downloads already in progress
0608     try {
0609         Uint64 db = downloader->bytesDownloaded();
0610         Uint64 cb = downloader->getDownloadedBytesOfCurrentChunksFile(tordir + "current_chunks");
0611         istats.prev_bytes_dl = db + cb;
0612 
0613         //  Out() << "Downloaded : " << BytesToString(db) << endl;
0614         //  Out() << "current_chunks : " << BytesToString(cb) << endl;
0615     } catch (Error &e) {
0616         // print out warning in case of failure
0617         Out(SYS_GEN | LOG_DEBUG) << "Warning : " << e.toString() << endl;
0618         istats.prev_bytes_dl = downloader->bytesDownloaded();
0619     }
0620 
0621     loadStats();
0622     updateStats();
0623     saveStats();
0624     stats.output_path = cman->getOutputPath();
0625     updateStatus();
0626 }
0627 
0628 void TorrentControl::setDisplayName(const QString &n)
0629 {
0630     display_name = n;
0631     saveStats();
0632 }
0633 
0634 bool TorrentControl::announceAllowed()
0635 {
0636     return psman != nullptr && stats.running;
0637 }
0638 
0639 void TorrentControl::updateTracker()
0640 {
0641     if (announceAllowed()) {
0642         psman->manualUpdate();
0643     }
0644 }
0645 
0646 void TorrentControl::scrapeTracker()
0647 {
0648     psman->scrape();
0649 }
0650 
0651 void TorrentControl::onNewPeer(Peer *p)
0652 {
0653     if (!stats.superseeding) {
0654         // Only send which chunks we have when we are not superseeding
0655         if (p->getStats().fast_extensions) {
0656             const BitSet &bs = cman->getBitSet();
0657             if (bs.allOn())
0658                 p->sendHaveAll();
0659             else if (bs.numOnBits() == 0)
0660                 p->sendHaveNone();
0661             else
0662                 p->sendBitSet(bs);
0663         } else {
0664             p->sendBitSet(cman->getBitSet());
0665         }
0666     }
0667 
0668     if (!stats.completed && !stats.paused)
0669         p->sendInterested();
0670 
0671     if (!stats.priv_torrent) {
0672         if (p->isDHTSupported())
0673             p->sendPort(Globals::instance().getDHT().getPort());
0674         else
0675             // WORKAROUND so we can contact µTorrent's DHT
0676             // They do not properly support the standard and do not turn on
0677             // the DHT bit in the handshake, so we just ping each peer by default.
0678             p->emitPortPacket();
0679     }
0680 
0681     // set group ID's for traffic shaping
0682     p->setGroupIDs(upload_gid, download_gid);
0683     downloader->addPieceDownloader(p->getPeerDownloader());
0684     if (tmon)
0685         tmon->peerAdded(p);
0686 }
0687 
0688 void TorrentControl::onPeerRemoved(Peer *p)
0689 {
0690     downloader->removePieceDownloader(p->getPeerDownloader());
0691     if (tmon)
0692         tmon->peerRemoved(p);
0693 }
0694 
0695 void TorrentControl::doChoking()
0696 {
0697     choke->update(stats.completed, stats);
0698 }
0699 
0700 bool TorrentControl::changeTorDir(const QString &new_dir)
0701 {
0702     int pos = tordir.lastIndexOf(bt::DirSeparator(), -2);
0703     if (pos == -1) {
0704         Out(SYS_GEN | LOG_DEBUG) << "Could not find torX part in " << tordir << endl;
0705         return false;
0706     }
0707 
0708     QString ntordir = new_dir + tordir.mid(pos + 1);
0709 
0710     Out(SYS_GEN | LOG_DEBUG) << tordir << " -> " << ntordir << endl;
0711     try {
0712         bt::Move(tordir, ntordir);
0713         old_tordir = tordir;
0714         tordir = ntordir;
0715     } catch (Error &err) {
0716         Out(SYS_GEN | LOG_IMPORTANT) << "Could not move " << tordir << " to " << ntordir << endl;
0717         return false;
0718     }
0719 
0720     cman->changeDataDir(tordir);
0721     return true;
0722 }
0723 
0724 bool TorrentControl::changeOutputDir(const QString &ndir, int flags)
0725 {
0726     // check if torrent is running and stop it before moving data
0727     QString new_dir = ndir;
0728     if (!new_dir.endsWith(bt::DirSeparator()))
0729         new_dir += bt::DirSeparator();
0730 
0731     try {
0732         QString nd;
0733         if (!(flags & bt::TorrentInterface::FULL_PATH)) {
0734             if (istats.custom_output_name) {
0735                 int slash_pos = stats.output_path.lastIndexOf(bt::DirSeparator(), -2);
0736                 nd = new_dir + stats.output_path.mid(slash_pos + 1);
0737             } else {
0738                 nd = new_dir + tor->getNameSuggestion();
0739             }
0740         } else {
0741             nd = new_dir;
0742         }
0743 
0744         if (stats.output_path != nd) {
0745             move_data_files_destination_path = nd;
0746             Job *j = nullptr;
0747             if (flags & bt::TorrentInterface::MOVE_FILES) {
0748                 if (stats.multi_file_torrent)
0749                     j = cman->moveDataFiles(nd);
0750                 else
0751                     j = cman->moveDataFiles(new_dir);
0752             }
0753 
0754             if (j) {
0755                 j->setTorrent(this);
0756                 connect(j, &Job::result, this, &TorrentControl::moveDataFilesFinished);
0757                 job_queue->enqueue(j);
0758                 return true;
0759             } else {
0760                 moveDataFilesFinished(nullptr);
0761             }
0762         } else {
0763             Out(SYS_GEN | LOG_NOTICE) << "Source is the same as destination, so doing nothing" << endl;
0764         }
0765     } catch (Error &err) {
0766         Out(SYS_GEN | LOG_IMPORTANT) << "Could not move " << stats.output_path << " to " << new_dir << ". Exception: " << endl;
0767         return false;
0768     }
0769 
0770     return true;
0771 }
0772 
0773 void TorrentControl::moveDataFilesFinished(KJob *kj)
0774 {
0775     Job *job = (Job *)kj;
0776     if (job)
0777         cman->moveDataFilesFinished(job);
0778 
0779     if (!job || (job && !job->error())) {
0780         cman->changeOutputPath(move_data_files_destination_path);
0781         outputdir = stats.output_path = move_data_files_destination_path;
0782         istats.custom_output_name = true;
0783 
0784         saveStats();
0785         Out(SYS_GEN | LOG_NOTICE) << "Data directory changed for torrent "
0786                                   << "'" << stats.torrent_name << "' to: " << move_data_files_destination_path << endl;
0787     } else if (job->error()) {
0788         Out(SYS_GEN | LOG_IMPORTANT) << "Could not move " << stats.output_path << " to " << move_data_files_destination_path << endl;
0789     }
0790 }
0791 
0792 bool TorrentControl::moveTorrentFiles(const QMap<TorrentFileInterface *, QString> &files)
0793 {
0794     try {
0795         Job *j = cman->moveDataFiles(files);
0796         if (j) {
0797             connect(j, &Job::result, this, &TorrentControl::moveDataFilesWithMapFinished);
0798             job_queue->enqueue(j);
0799         }
0800 
0801     } catch (Error &err) {
0802         return false;
0803     }
0804 
0805     return true;
0806 }
0807 
0808 void TorrentControl::moveDataFilesWithMapFinished(KJob *j)
0809 {
0810     if (!j)
0811         return;
0812 
0813     MoveDataFilesJob *job = (MoveDataFilesJob *)j;
0814     cman->moveDataFilesFinished(job->fileMap(), job);
0815     Out(SYS_GEN | LOG_NOTICE) << "Move of data files completed " << endl;
0816 }
0817 
0818 void TorrentControl::rollback()
0819 {
0820     try {
0821         bt::Move(tordir, old_tordir);
0822         tordir = old_tordir;
0823         cman->changeDataDir(tordir);
0824     } catch (Error &err) {
0825         Out(SYS_GEN | LOG_IMPORTANT) << "Could not move " << tordir << " to " << old_tordir << endl;
0826     }
0827 }
0828 
0829 void TorrentControl::updateStatus()
0830 {
0831     TorrentStatus old = stats.status;
0832     if (stats.stopped_by_error)
0833         stats.status = ERROR;
0834     else if (job_queue->currentJob() && job_queue->currentJob()->torrentStatus() != INVALID_STATUS)
0835         stats.status = job_queue->currentJob()->torrentStatus();
0836     else if (stats.queued)
0837         stats.status = QUEUED;
0838     else if (stats.completed && (overMaxRatio() || overMaxSeedTime()))
0839         stats.status = SEEDING_COMPLETE;
0840     else if (!stats.running && stats.completed)
0841         stats.status = DOWNLOAD_COMPLETE;
0842     else if (!stats.started)
0843         stats.status = NOT_STARTED;
0844     else if (!stats.running)
0845         stats.status = STOPPED;
0846     else if (stats.running && stats.paused)
0847         stats.status = PAUSED;
0848     else if (stats.running && stats.completed)
0849         stats.status = stats.superseeding ? SUPERSEEDING : SEEDING;
0850     else if (stats.running)
0851         // protocol messages are also included in speed calculation, so lets not compare with 0
0852         stats.status = downloader->downloadRate() > 100 ? DOWNLOADING : STALLED;
0853 
0854     if (old != stats.status)
0855         statusChanged(this);
0856 }
0857 
0858 const BitSet &TorrentControl::downloadedChunksBitSet() const
0859 {
0860     if (cman)
0861         return cman->getBitSet();
0862     else
0863         return BitSet::null;
0864 }
0865 
0866 const BitSet &TorrentControl::availableChunksBitSet() const
0867 {
0868     if (!pman)
0869         return BitSet::null;
0870     else
0871         return pman->getAvailableChunksBitSet();
0872 }
0873 
0874 const BitSet &TorrentControl::excludedChunksBitSet() const
0875 {
0876     if (!cman)
0877         return BitSet::null;
0878     else
0879         return cman->getExcludedBitSet();
0880 }
0881 
0882 const BitSet &TorrentControl::onlySeedChunksBitSet() const
0883 {
0884     if (!cman)
0885         return BitSet::null;
0886     else
0887         return cman->getOnlySeedBitSet();
0888 }
0889 
0890 void TorrentControl::saveStats()
0891 {
0892     if (loading_stats) // don't save while we are loading
0893         return;
0894 
0895     if (!stats_file)
0896         stats_file = new StatsFile(tordir + "stats");
0897 
0898     stats_file->write("OUTPUTDIR", cman->getDataDir());
0899     stats_file->write("COMPLETEDDIR", completed_dir);
0900 
0901     if (cman->getDataDir() != outputdir)
0902         outputdir = cman->getDataDir();
0903 
0904     stats_file->write("UPLOADED", QString::number(uploader->bytesUploaded()));
0905 
0906     if (stats.running) {
0907         QDateTime now = QDateTime::currentDateTime();
0908         if (!stats.completed)
0909             stats_file->write("RUNNING_TIME_DL", QString("%1").arg(istats.running_time_dl + istats.time_started_dl.secsTo(now)));
0910         else
0911             stats_file->write("RUNNING_TIME_DL", QString("%1").arg(istats.running_time_dl));
0912         stats_file->write("RUNNING_TIME_UL", QString("%1").arg(istats.running_time_ul + istats.time_started_ul.secsTo(now)));
0913     } else {
0914         stats_file->write("RUNNING_TIME_DL", QString("%1").arg(istats.running_time_dl));
0915         stats_file->write("RUNNING_TIME_UL", QString("%1").arg(istats.running_time_ul));
0916     }
0917 
0918     stats_file->write("QM_CAN_START", stats.qm_can_start ? "1" : "0");
0919     stats_file->write("PRIORITY", QString("%1").arg(istats.priority));
0920     stats_file->write("AUTOSTART", QString("%1").arg(stats.autostart));
0921     stats_file->write("IMPORTED", QString("%1").arg(stats.imported_bytes));
0922     stats_file->write("CUSTOM_OUTPUT_NAME", istats.custom_output_name ? "1" : "0");
0923     stats_file->write("MAX_RATIO", QString("%1").arg(stats.max_share_ratio, 0, 'f', 2));
0924     stats_file->write("MAX_SEED_TIME", QString::number(stats.max_seed_time));
0925     stats_file->write("RESTART_DISK_PREALLOCATION", prealloc ? "1" : "0");
0926     stats_file->write("AUTO_STOPPED", stats.auto_stopped ? "1" : "0");
0927 
0928     if (!stats.priv_torrent) {
0929         // save dht and pex
0930         stats_file->write("DHT", isFeatureEnabled(DHT_FEATURE) ? "1" : "0");
0931         stats_file->write("UT_PEX", isFeatureEnabled(UT_PEX_FEATURE) ? "1" : "0");
0932     }
0933 
0934     stats_file->write("UPLOAD_LIMIT", QString::number(upload_limit));
0935     stats_file->write("DOWNLOAD_LIMIT", QString::number(download_limit));
0936     stats_file->write("ENCODING", QString(tor->getTextCodec()->name()));
0937     stats_file->write("ASSURED_UPLOAD_SPEED", QString::number(assured_upload_speed));
0938     stats_file->write("ASSURED_DOWNLOAD_SPEED", QString::number(assured_download_speed));
0939     if (!user_modified_name.isEmpty())
0940         stats_file->write("USER_MODIFIED_NAME", user_modified_name);
0941     stats_file->write("DISPLAY_NAME", display_name);
0942     stats_file->write("URL", url.toDisplayString());
0943 
0944     stats_file->write("TIME_ADDED", QString("%1").arg(stats.time_added.toSecsSinceEpoch()));
0945     stats_file->write("SUPERSEEDING", stats.superseeding ? "1" : "0");
0946 
0947     stats_file->sync();
0948 }
0949 
0950 void TorrentControl::loadStats()
0951 {
0952     if (!bt::Exists(tordir + "stats")) {
0953         setFeatureEnabled(DHT_FEATURE, true);
0954         setFeatureEnabled(UT_PEX_FEATURE, true);
0955         return;
0956     }
0957 
0958     RecursiveEntryGuard guard(&loading_stats);
0959     if (!stats_file)
0960         stats_file = new StatsFile(tordir + "stats");
0961 
0962     Uint64 val = stats_file->readUint64("UPLOADED");
0963     // stats.session_bytes_uploaded will be calculated based upon prev_bytes_ul
0964     // seeing that this will change here, we need to save it
0965     istats.session_bytes_uploaded = stats.session_bytes_uploaded;
0966     istats.prev_bytes_ul = val;
0967     uploader->setBytesUploaded(val);
0968 
0969     istats.running_time_dl = stats_file->readULong("RUNNING_TIME_DL");
0970     istats.running_time_ul = stats_file->readULong("RUNNING_TIME_UL");
0971     // make sure runtime ul is always equal or largen then running time dl
0972     // in case something got corrupted
0973     if (istats.running_time_ul < istats.running_time_dl)
0974         istats.running_time_ul = istats.running_time_dl;
0975 
0976     outputdir = stats_file->readString("OUTPUTDIR").trimmed();
0977     if (stats_file->hasKey("CUSTOM_OUTPUT_NAME") && stats_file->readULong("CUSTOM_OUTPUT_NAME") == 1) {
0978         istats.custom_output_name = true;
0979     }
0980 
0981     if (stats_file->hasKey("COMPLETEDDIR")) {
0982         completed_dir = stats_file->readString("COMPLETEDDIR");
0983         if (completed_dir == outputdir)
0984             completed_dir = QString();
0985     }
0986 
0987     if (stats_file->hasKey("USER_MODIFIED_NAME"))
0988         user_modified_name = stats_file->readString("USER_MODIFIED_NAME");
0989 
0990     if (stats_file->hasKey("DISPLAY_NAME"))
0991         display_name = stats_file->readString("DISPLAY_NAME");
0992 
0993     istats.priority = stats_file->readInt("PRIORITY");
0994     stats.autostart = stats_file->readBoolean("AUTOSTART");
0995     stats.imported_bytes = stats_file->readUint64("IMPORTED");
0996     stats.max_share_ratio = stats_file->readFloat("MAX_RATIO");
0997     stats.max_seed_time = stats_file->readFloat("MAX_SEED_TIME");
0998     stats.qm_can_start = stats_file->readBoolean("QM_CAN_START");
0999     stats.auto_stopped = stats_file->readBoolean("AUTO_STOPPED");
1000 
1001     if (stats_file->hasKey("RESTART_DISK_PREALLOCATION"))
1002         prealloc = stats_file->readString("RESTART_DISK_PREALLOCATION") == "1";
1003 
1004     if (!stats.priv_torrent) {
1005         if (stats_file->hasKey("DHT"))
1006             istats.dht_on = stats_file->readBoolean("DHT");
1007         else
1008             istats.dht_on = true;
1009 
1010         setFeatureEnabled(DHT_FEATURE, istats.dht_on);
1011         if (stats_file->hasKey("UT_PEX"))
1012             setFeatureEnabled(UT_PEX_FEATURE, stats_file->readBoolean("UT_PEX"));
1013     }
1014 
1015     QString codec = stats_file->readString("ENCODING");
1016     if (codec.length() > 0) {
1017         QTextCodec *cod = QTextCodec::codecForName(codec.toLocal8Bit());
1018         if (cod)
1019             changeTextCodec(cod);
1020     }
1021 
1022     Uint32 aup = stats_file->readInt("ASSURED_UPLOAD_SPEED");
1023     Uint32 adown = stats_file->readInt("ASSURED_DOWNLOAD_SPEED");
1024     Uint32 up = stats_file->readInt("UPLOAD_LIMIT");
1025     Uint32 down = stats_file->readInt("DOWNLOAD_LIMIT");
1026     setDownloadProps(down, adown);
1027     setUploadProps(up, aup);
1028     pman->setGroupIDs(upload_gid, download_gid);
1029 
1030     url = QUrl(stats_file->readString("URL"));
1031 
1032     if (stats_file->hasKey("TIME_ADDED"))
1033         stats.time_added.setSecsSinceEpoch(stats_file->readULong("TIME_ADDED"));
1034     else
1035         stats.time_added = QDateTime::currentDateTime();
1036 
1037     bool ss = stats_file->hasKey("SUPERSEEDING") && stats_file->readBoolean("SUPERSEEDING");
1038     setSuperSeeding(ss);
1039 }
1040 
1041 void TorrentControl::loadOutputDir()
1042 {
1043     if (!stats_file)
1044         stats_file = new StatsFile(tordir + "stats");
1045 
1046     if (!stats_file->hasKey("OUTPUTDIR"))
1047         return;
1048 
1049     outputdir = stats_file->readString("OUTPUTDIR").trimmed();
1050     if (stats_file->hasKey("CUSTOM_OUTPUT_NAME") && stats_file->readULong("CUSTOM_OUTPUT_NAME") == 1) {
1051         istats.custom_output_name = true;
1052     }
1053 }
1054 
1055 void TorrentControl::loadEncoding()
1056 {
1057     if (!stats_file)
1058         stats_file = new StatsFile(tordir + "stats");
1059     if (!stats_file->hasKey("ENCODING"))
1060         return;
1061 
1062     QString codec = stats_file->readString("ENCODING");
1063     if (codec.length() > 0) {
1064         QTextCodec *cod = QTextCodec::codecForName(codec.toLocal8Bit());
1065         if (cod)
1066             changeTextCodec(cod);
1067     }
1068 }
1069 
1070 bool TorrentControl::readyForPreview() const
1071 {
1072     if (tor->isMultiFile() || !tor->isMultimedia())
1073         return false;
1074 
1075     Uint32 preview_range = cman->previewChunkRangeSize();
1076     if (preview_range == 0)
1077         return false;
1078 
1079     const BitSet &bs = downloadedChunksBitSet();
1080     for (Uint32 i = 0; i < qMin(preview_range, bs.getNumBits()); i++) {
1081         if (!bs.get(i))
1082             return false;
1083     }
1084     return true;
1085 }
1086 
1087 bool TorrentControl::isMultimedia() const
1088 {
1089     return !tor->isMultiFile() && tor->isMultimedia();
1090 }
1091 
1092 void TorrentControl::updateStats()
1093 {
1094     stats.num_chunks_downloading = downloader ? downloader->numActiveDownloads() : 0;
1095     stats.num_peers = pman ? pman->getNumConnectedPeers() : 0;
1096     stats.upload_rate = uploader && stats.running ? uploader->uploadRate() : 0;
1097     stats.download_rate = downloader && stats.running ? downloader->downloadRate() : 0;
1098     stats.bytes_left = cman ? cman->bytesLeft() : 0;
1099     stats.bytes_left_to_download = cman ? cman->bytesLeftToDownload() : 0;
1100     stats.bytes_uploaded = uploader ? uploader->bytesUploaded() : 0;
1101     stats.bytes_downloaded = downloader ? downloader->bytesDownloaded() : 0;
1102     stats.total_chunks = tor ? tor->getNumChunks() : 0;
1103     stats.num_chunks_downloaded = cman ? cman->chunksDownloaded() : 0;
1104     stats.num_chunks_excluded = cman ? cman->chunksExcluded() : 0;
1105     stats.chunk_size = tor ? tor->getChunkSize() : 0;
1106     stats.num_chunks_left = cman ? cman->chunksLeft() : 0;
1107     stats.total_bytes_to_download = (tor && cman) ? tor->getTotalSize() - cman->bytesExcluded() : 0;
1108 
1109     if (stats.bytes_downloaded >= istats.prev_bytes_dl)
1110         stats.session_bytes_downloaded = stats.bytes_downloaded - istats.prev_bytes_dl;
1111     else
1112         stats.session_bytes_downloaded = 0;
1113 
1114     if (stats.bytes_uploaded >= istats.prev_bytes_ul)
1115         stats.session_bytes_uploaded = (stats.bytes_uploaded - istats.prev_bytes_ul) + istats.session_bytes_uploaded;
1116     else
1117         stats.session_bytes_uploaded = istats.session_bytes_uploaded;
1118 
1119     getSeederInfo(stats.seeders_total, stats.seeders_connected_to);
1120     getLeecherInfo(stats.leechers_total, stats.leechers_connected_to);
1121 }
1122 
1123 void TorrentControl::trackerScrapeDone()
1124 {
1125     stats.seeders_total = psman->getNumSeeders();
1126     stats.leechers_total = psman->getNumLeechers();
1127 }
1128 
1129 void TorrentControl::getSeederInfo(Uint32 &total, Uint32 &connected_to) const
1130 {
1131     total = 0;
1132     connected_to = 0;
1133     if (!pman || !psman)
1134         return;
1135 
1136     connected_to = pman->getNumConnectedSeeders();
1137     total = psman->getNumSeeders();
1138     if (total == 0)
1139         total = connected_to;
1140 }
1141 
1142 void TorrentControl::getLeecherInfo(Uint32 &total, Uint32 &connected_to) const
1143 {
1144     total = 0;
1145     connected_to = 0;
1146     if (!pman || !psman)
1147         return;
1148 
1149     connected_to = pman->getNumConnectedLeechers();
1150     total = psman->getNumLeechers();
1151     if (total == 0)
1152         total = connected_to;
1153 }
1154 
1155 Uint32 TorrentControl::getRunningTimeDL() const
1156 {
1157     if (!stats.running || stats.completed || stats.paused)
1158         return istats.running_time_dl;
1159     else
1160         return istats.running_time_dl + istats.time_started_dl.secsTo(QDateTime::currentDateTime());
1161 }
1162 
1163 Uint32 TorrentControl::getRunningTimeUL() const
1164 {
1165     if (!stats.running || stats.paused)
1166         return istats.running_time_ul;
1167     else
1168         return istats.running_time_ul + istats.time_started_ul.secsTo(QDateTime::currentDateTime());
1169 }
1170 
1171 Uint32 TorrentControl::getNumFiles() const
1172 {
1173     if (tor && tor->isMultiFile())
1174         return tor->getNumFiles();
1175     else
1176         return 0;
1177 }
1178 
1179 TorrentFileInterface &TorrentControl::getTorrentFile(Uint32 index)
1180 {
1181     if (tor)
1182         return tor->getFile(index);
1183     else
1184         return TorrentFile::null;
1185 }
1186 
1187 const TorrentFileInterface &TorrentControl::getTorrentFile(Uint32 index) const
1188 {
1189     if (tor)
1190         return tor->getFile(index);
1191     else
1192         return TorrentFile::null;
1193 }
1194 
1195 void TorrentControl::setPriority(int p)
1196 {
1197     istats.priority = p;
1198     if (!stats_file)
1199         stats_file = new StatsFile(tordir + "stats");
1200 
1201     stats_file->write("PRIORITY", QString("%1").arg(istats.priority));
1202     updateStatus();
1203 }
1204 
1205 void TorrentControl::setMaxShareRatio(float ratio)
1206 {
1207     if (ratio == 1.00f) {
1208         if (stats.max_share_ratio != ratio)
1209             stats.max_share_ratio = ratio;
1210     } else
1211         stats.max_share_ratio = ratio;
1212 
1213     saveStats();
1214     Q_EMIT maxRatioChanged(this);
1215 }
1216 
1217 void TorrentControl::setMaxSeedTime(float hours)
1218 {
1219     stats.max_seed_time = hours;
1220     saveStats();
1221 }
1222 
1223 bool TorrentControl::overMaxRatio()
1224 {
1225     return stats.overMaxRatio();
1226 }
1227 
1228 bool TorrentControl::overMaxSeedTime()
1229 {
1230     if (stats.completed && stats.max_seed_time > 0) {
1231         Uint32 dl = getRunningTimeDL();
1232         Uint32 ul = getRunningTimeUL();
1233         if ((ul - dl) / 3600.0f > stats.max_seed_time)
1234             return true;
1235     }
1236 
1237     return false;
1238 }
1239 
1240 TrackersList *TorrentControl::getTrackersList()
1241 {
1242     return psman;
1243 }
1244 
1245 const TrackersList *TorrentControl::getTrackersList() const
1246 {
1247     return psman;
1248 }
1249 
1250 Job *TorrentControl::startDataCheck(bool auto_import, bt::Uint32 from, bt::Uint32 to)
1251 {
1252     Job *j = new DataCheckerJob(auto_import, this, from, to);
1253     job_queue->enqueue(j);
1254     return j;
1255 }
1256 
1257 void TorrentControl::beforeDataCheck()
1258 {
1259     stats.status = CHECKING_DATA;
1260     stats.num_corrupted_chunks = 0; // reset the number of corrupted chunks found
1261     statusChanged(this);
1262 }
1263 
1264 void TorrentControl::afterDataCheck(DataCheckerJob *job, const BitSet &result)
1265 {
1266     bool completed = stats.completed;
1267     if (job && !job->isStopped()) {
1268         downloader->dataChecked(result, job->firstChunk(), job->lastChunk());
1269         // update chunk manager
1270         cman->dataChecked(result, job->firstChunk(), job->lastChunk());
1271         if (job->isAutoImport()) {
1272             downloader->recalcDownloaded();
1273             stats.imported_bytes = downloader->bytesDownloaded();
1274             stats.completed = cman->completed();
1275         } else {
1276             Uint64 downloaded = stats.bytes_downloaded;
1277             downloader->recalcDownloaded();
1278             updateStats();
1279             if (stats.bytes_downloaded > downloaded)
1280                 stats.imported_bytes = stats.bytes_downloaded - downloaded;
1281 
1282             stats.completed = cman->completed();
1283             pman->setPartialSeed(!cman->haveAllChunks() && cman->chunksLeft() == 0);
1284         }
1285     }
1286 
1287     saveStats();
1288     updateStats();
1289     Out(SYS_GEN | LOG_NOTICE) << "Data check finished" << endl;
1290     updateStatus();
1291     dataCheckFinished();
1292 
1293     if (stats.completed != completed) {
1294         // Tell QM to redo queue and emit finished signal
1295         // seeder might have become downloader, so
1296         // queue might need to be redone
1297         // use QTimer because we need to ensure this is run after the JobQueue removes the job
1298         QTimer::singleShot(0, this, SIGNAL(updateQueue()));
1299         if (stats.completed)
1300             QTimer::singleShot(0, this, &TorrentControl::emitFinished);
1301     }
1302 }
1303 
1304 void TorrentControl::emitFinished()
1305 {
1306     finished(this);
1307 }
1308 
1309 void TorrentControl::markExistingFilesAsDownloaded()
1310 {
1311     cman->markExistingFilesAsDownloaded();
1312     downloader->recalcDownloaded();
1313     stats.imported_bytes = downloader->bytesDownloaded();
1314     if (cman->haveAllChunks())
1315         stats.completed = true;
1316 
1317     updateStats();
1318 }
1319 
1320 bool TorrentControl::hasExistingFiles() const
1321 {
1322     return cman->hasExistingFiles();
1323 }
1324 
1325 bool TorrentControl::isStorageMounted(QStringList &missing)
1326 {
1327     return cman->isStorageMounted(missing);
1328 }
1329 
1330 bool TorrentControl::hasMissingFiles(QStringList &sl)
1331 {
1332     return cman->hasMissingFiles(sl);
1333 }
1334 
1335 void TorrentControl::recreateMissingFiles()
1336 {
1337     try {
1338         cman->recreateMissingFiles();
1339         prealloc = true; // set prealloc to true so files will be truncated again
1340         downloader->dataChecked(cman->getBitSet(), 0, tor->getNumChunks() - 1); // update chunk selector
1341     } catch (Error &err) {
1342         onIOError(err.toString());
1343         throw;
1344     }
1345 }
1346 
1347 void TorrentControl::dndMissingFiles()
1348 {
1349     try {
1350         cman->dndMissingFiles();
1351         prealloc = true; // set prealloc to true so files will be truncated again
1352         missingFilesMarkedDND(this);
1353         downloader->dataChecked(cman->getBitSet(), 0, tor->getNumChunks() - 1); // update chunk selector
1354     } catch (Error &err) {
1355         onIOError(err.toString());
1356         throw;
1357     }
1358 }
1359 
1360 void TorrentControl::handleError(const QString &err)
1361 {
1362     onIOError(err);
1363 }
1364 
1365 Uint32 TorrentControl::getNumDHTNodes() const
1366 {
1367     return tor->getNumDHTNodes();
1368 }
1369 
1370 const DHTNode &TorrentControl::getDHTNode(Uint32 i) const
1371 {
1372     return tor->getDHTNode(i);
1373 }
1374 
1375 void TorrentControl::deleteDataFiles()
1376 {
1377     if (!cman)
1378         return;
1379 
1380     Job *job = cman->deleteDataFiles();
1381     if (job)
1382         job->start(); // don't use queue, this is only done when removing the torrent
1383 }
1384 
1385 const bt::SHA1Hash &TorrentControl::getInfoHash() const
1386 {
1387     return tor->getInfoHash();
1388 }
1389 
1390 void TorrentControl::addPeerSource(PeerSource *ps)
1391 {
1392     if (psman)
1393         psman->addPeerSource(ps);
1394 }
1395 
1396 void TorrentControl::removePeerSource(PeerSource *ps)
1397 {
1398     if (psman)
1399         psman->removePeerSource(ps);
1400 }
1401 
1402 void TorrentControl::corrupted(Uint32 chunk)
1403 {
1404     // make sure we will redownload the chunk
1405     downloader->corrupted(chunk);
1406     if (stats.completed)
1407         stats.completed = false;
1408 
1409     // emit signal to show a systray message
1410     stats.num_corrupted_chunks++;
1411     corruptedDataFound(this);
1412 }
1413 
1414 int TorrentControl::getETA()
1415 {
1416     return m_eta->estimate();
1417 }
1418 
1419 const bt::PeerID &TorrentControl::getOwnPeerID() const
1420 {
1421     return tor->getPeerID();
1422 }
1423 
1424 bool TorrentControl::isFeatureEnabled(TorrentFeature tf)
1425 {
1426     switch (tf) {
1427     case DHT_FEATURE:
1428         return psman->dhtStarted();
1429     case UT_PEX_FEATURE:
1430         return pman->isPexEnabled();
1431     default:
1432         return false;
1433     }
1434 }
1435 
1436 void TorrentControl::setFeatureEnabled(TorrentFeature tf, bool on)
1437 {
1438     switch (tf) {
1439     case DHT_FEATURE:
1440         if (on) {
1441             if (!stats.priv_torrent) {
1442                 psman->addDHT();
1443                 istats.dht_on = psman->dhtStarted();
1444                 saveStats();
1445             }
1446         } else {
1447             psman->removeDHT();
1448             istats.dht_on = false;
1449             saveStats();
1450         }
1451         break;
1452     case UT_PEX_FEATURE:
1453         if (on) {
1454             if (!stats.priv_torrent && !pman->isPexEnabled()) {
1455                 pman->setPexEnabled(true);
1456             }
1457         } else {
1458             pman->setPexEnabled(false);
1459         }
1460         break;
1461     }
1462 }
1463 
1464 void TorrentControl::createFiles()
1465 {
1466     cman->createFiles(true);
1467     stats.output_path = cman->getOutputPath();
1468 }
1469 
1470 bool TorrentControl::checkDiskSpace(bool emit_sig)
1471 {
1472     last_diskspace_check = bt::CurrentTime();
1473 
1474     // calculate free disk space
1475     Uint64 bytes_free = 0;
1476     if (FreeDiskSpace(getDataDir(), bytes_free)) {
1477         Out(SYS_GEN | LOG_DEBUG) << "FreeBytes " << BytesToString(bytes_free) << endl;
1478         Uint64 bytes_to_download = stats.total_bytes_to_download;
1479         Uint64 downloaded = 0;
1480         try {
1481             downloaded = cman->diskUsage();
1482             Out(SYS_GEN | LOG_DEBUG) << "Downloaded " << BytesToString(downloaded) << endl;
1483         } catch (bt::Error &err) {
1484             Out(SYS_GEN | LOG_DEBUG) << "Error : " << err.toString() << endl;
1485         }
1486         Uint64 remaining = 0;
1487         if (downloaded <= bytes_to_download)
1488             remaining = bytes_to_download - downloaded;
1489 
1490         Out(SYS_GEN | LOG_DEBUG) << "Remaining " << BytesToString(remaining) << endl;
1491         if (remaining > bytes_free) {
1492             bool toStop = bytes_free < (Uint64)min_diskspace * 1024 * 1024;
1493 
1494             // if we don't need to stop the torrent, only emit the signal once
1495             // so that we do bother the user continuously
1496             if (emit_sig && (toStop || !istats.diskspace_warning_emitted)) {
1497                 Q_EMIT diskSpaceLow(this, toStop);
1498                 istats.diskspace_warning_emitted = true;
1499             }
1500 
1501             if (!stats.running) {
1502                 stats.status = NO_SPACE_LEFT;
1503                 statusChanged(this);
1504             }
1505 
1506             return false;
1507         }
1508     }
1509 
1510     return true;
1511 }
1512 
1513 void TorrentControl::setUploadProps(Uint32 limit, Uint32 rate)
1514 {
1515     net::SocketMonitor &smon = net::SocketMonitor::instance();
1516     if (upload_gid) {
1517         if (!limit && !rate) {
1518             smon.removeGroup(net::SocketMonitor::UPLOAD_GROUP, upload_gid);
1519             upload_gid = 0;
1520         } else {
1521             smon.setGroupLimit(net::SocketMonitor::UPLOAD_GROUP, upload_gid, limit);
1522             smon.setGroupAssuredRate(net::SocketMonitor::UPLOAD_GROUP, upload_gid, rate);
1523         }
1524     } else {
1525         if (limit || rate) {
1526             upload_gid = smon.newGroup(net::SocketMonitor::UPLOAD_GROUP, limit, rate);
1527         }
1528     }
1529 
1530     upload_limit = limit;
1531     assured_upload_speed = rate;
1532 }
1533 
1534 void TorrentControl::setDownloadProps(Uint32 limit, Uint32 rate)
1535 {
1536     net::SocketMonitor &smon = net::SocketMonitor::instance();
1537     if (download_gid) {
1538         if (!limit && !rate) {
1539             smon.removeGroup(net::SocketMonitor::DOWNLOAD_GROUP, download_gid);
1540             download_gid = 0;
1541         } else {
1542             smon.setGroupLimit(net::SocketMonitor::DOWNLOAD_GROUP, download_gid, limit);
1543             smon.setGroupAssuredRate(net::SocketMonitor::DOWNLOAD_GROUP, download_gid, rate);
1544         }
1545     } else {
1546         if (limit || rate) {
1547             download_gid = smon.newGroup(net::SocketMonitor::DOWNLOAD_GROUP, limit, rate);
1548         }
1549     }
1550 
1551     download_limit = limit;
1552     assured_download_speed = rate;
1553 }
1554 
1555 void TorrentControl::setTrafficLimits(Uint32 up, Uint32 down)
1556 {
1557     setDownloadProps(down, assured_download_speed);
1558     setUploadProps(up, assured_upload_speed);
1559     saveStats();
1560     pman->setGroupIDs(upload_gid, download_gid);
1561     downloader->setGroupIDs(upload_gid, download_gid);
1562 }
1563 
1564 void TorrentControl::getTrafficLimits(Uint32 &up, Uint32 &down)
1565 {
1566     up = upload_limit;
1567     down = download_limit;
1568 }
1569 
1570 void TorrentControl::setAssuredSpeeds(Uint32 up, Uint32 down)
1571 {
1572     setDownloadProps(download_limit, down);
1573     setUploadProps(upload_limit, up);
1574     saveStats();
1575     pman->setGroupIDs(upload_gid, download_gid);
1576     downloader->setGroupIDs(upload_gid, download_gid);
1577 }
1578 
1579 void TorrentControl::getAssuredSpeeds(Uint32 &up, Uint32 &down)
1580 {
1581     up = assured_upload_speed;
1582     down = assured_download_speed;
1583 }
1584 
1585 const PeerManager *TorrentControl::getPeerMgr() const
1586 {
1587     return pman;
1588 }
1589 
1590 void TorrentControl::preallocFinished(const QString &error, bool completed)
1591 {
1592     Out(SYS_GEN | LOG_DEBUG) << "preallocFinished " << error << " " << completed << endl;
1593     if (!error.isEmpty() || !completed) {
1594         // upon error just call onIOError and return
1595         if (!error.isEmpty())
1596             onIOError(error);
1597         prealloc = true; // still need to do preallocation
1598     } else {
1599         // continue the startup of the torrent
1600         prealloc = false;
1601         stats.status = NOT_STARTED;
1602         saveStats();
1603         continueStart();
1604         statusChanged(this);
1605     }
1606 }
1607 
1608 const QTextCodec *TorrentControl::getTextCodec() const
1609 {
1610     if (!tor)
1611         return nullptr;
1612     else
1613         return tor->getTextCodec();
1614 }
1615 
1616 void TorrentControl::changeTextCodec(QTextCodec *tc)
1617 {
1618     if (tor) {
1619         tor->changeTextCodec(tc);
1620         stats.torrent_name = tor->getNameSuggestion();
1621     }
1622 }
1623 
1624 void TorrentControl::setCacheFactory(CacheFactory *cf)
1625 {
1626     cache_factory = cf;
1627 }
1628 
1629 Uint32 TorrentControl::getNumWebSeeds() const
1630 {
1631     return downloader->getNumWebSeeds();
1632 }
1633 
1634 const WebSeedInterface *TorrentControl::getWebSeed(Uint32 i) const
1635 {
1636     return downloader->getWebSeed(i);
1637 }
1638 
1639 WebSeedInterface *TorrentControl::getWebSeed(Uint32 i)
1640 {
1641     return downloader->getWebSeed(i);
1642 }
1643 
1644 bool TorrentControl::addWebSeed(const QUrl &url)
1645 {
1646     WebSeed *ws = downloader->addWebSeed(url);
1647     if (ws) {
1648         downloader->saveWebSeeds(tordir + "webseeds");
1649         ws->setGroupIDs(upload_gid, download_gid); // make sure webseed has proper group ID
1650     }
1651     return ws != nullptr;
1652 }
1653 
1654 bool TorrentControl::removeWebSeed(const QUrl &url)
1655 {
1656     bool ret = downloader->removeWebSeed(url);
1657     if (ret)
1658         downloader->saveWebSeeds(tordir + "webseeds");
1659     return ret;
1660 }
1661 
1662 void TorrentControl::setUserModifiedFileName(const QString &n)
1663 {
1664     TorrentInterface::setUserModifiedFileName(n);
1665     QString path = getDataDir();
1666     if (!path.endsWith(bt::DirSeparator()))
1667         path += bt::DirSeparator();
1668 
1669     cman->changeOutputPath(path + n);
1670     outputdir = stats.output_path = path + n;
1671     istats.custom_output_name = true;
1672 }
1673 
1674 void TorrentControl::downloaded(Uint32 chunk)
1675 {
1676     chunkDownloaded(this, chunk);
1677 }
1678 
1679 void TorrentControl::moveToCompletedDir()
1680 {
1681     QString outdir = completed_dir;
1682     if (!outdir.endsWith(bt::DirSeparator()))
1683         outdir += bt::DirSeparator();
1684 
1685     changeOutputDir(outdir, bt::TorrentInterface::MOVE_FILES);
1686 }
1687 
1688 void TorrentControl::setAllowedToStart(bool on)
1689 {
1690     stats.qm_can_start = on;
1691     if (on && stats.stopped_by_error) {
1692         // clear the error so user can restart the torrent
1693         stats.stopped_by_error = false;
1694     }
1695     saveStats();
1696 }
1697 
1698 void TorrentControl::setQueued(bool queued)
1699 {
1700     stats.queued = queued;
1701     updateStatus();
1702 }
1703 
1704 QString TorrentControl::getComments() const
1705 {
1706     return tor->getComments();
1707 }
1708 
1709 void TorrentControl::allJobsDone()
1710 {
1711     updateStatus();
1712     // update the QM to be sure
1713     updateQueue();
1714     runningJobsDone(this);
1715 }
1716 
1717 void TorrentControl::setChunkSelector(ChunkSelectorInterface *csel)
1718 {
1719     if (!downloader)
1720         return;
1721 
1722     if (csel)
1723         downloader->setChunkSelector(csel);
1724     else
1725         downloader->setChunkSelector(nullptr);
1726 }
1727 
1728 void TorrentControl::networkUp()
1729 {
1730     psman->manualUpdate();
1731     pman->killStalePeers();
1732 }
1733 
1734 bool TorrentControl::preallocate()
1735 {
1736     // only start preallocation if we are allowed by the settings
1737     if (Cache::preallocationEnabled() && !cman->haveAllChunks()) {
1738         Out(SYS_GEN | LOG_NOTICE) << "Pre-allocating diskspace" << endl;
1739         stats.running = true;
1740         job_queue->enqueue(new PreallocationJob(cman, this));
1741         updateStatus();
1742         return true;
1743     } else {
1744         prealloc = false;
1745     }
1746 
1747     return false;
1748 }
1749 
1750 void TorrentControl::downloadPriorityChanged(TorrentFile *tf, Priority newpriority, Priority oldpriority)
1751 {
1752     if (cman)
1753         cman->downloadPriorityChanged(tf, newpriority, oldpriority);
1754 
1755     // If a file has been reenabled, preallocate it and update stats
1756     if (oldpriority == EXCLUDED) {
1757         prealloc = true;
1758         stats.completed = false;
1759         updateStatus();
1760         updateStats();
1761         // Trigger an update of the queue, so it can be restarted again, if it was auto stopped
1762         updateQueue();
1763     }
1764 }
1765 
1766 void TorrentControl::setSuperSeeding(bool on)
1767 {
1768     if (stats.superseeding == on)
1769         return;
1770 
1771     stats.superseeding = on;
1772     if (on) {
1773         if (stats.running && stats.completed)
1774             pman->setSuperSeeding(true, cman->getBitSet());
1775     } else {
1776         pman->setSuperSeeding(false, cman->getBitSet());
1777     }
1778 
1779     saveStats();
1780 }
1781 
1782 TorrentFileStream::Ptr TorrentControl::createTorrentFileStream(Uint32 index, bool streaming_mode, QObject *parent)
1783 {
1784     if (streaming_mode && !stream.toStrongRef().isNull())
1785         return TorrentFileStream::Ptr(nullptr);
1786 
1787     if (stats.multi_file_torrent) {
1788         if (index >= tor->getNumFiles())
1789             return TorrentFileStream::Ptr(nullptr);
1790 
1791         TorrentFileStream::Ptr ptr(new TorrentFileStream(this, index, cman, streaming_mode, parent));
1792         if (streaming_mode)
1793             stream = ptr;
1794         return ptr;
1795     } else {
1796         TorrentFileStream::Ptr ptr(new TorrentFileStream(this, cman, streaming_mode, parent));
1797         if (streaming_mode)
1798             stream = ptr;
1799         return ptr;
1800     }
1801 }
1802 
1803 }
1804 
1805 #include "moc_torrentcontrol.cpp"