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"