File indexing completed on 2024-11-24 04:31:17
0001 /* 0002 SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 #include "downloader.h" 0007 0008 #include <QFile> 0009 #include <QTextStream> 0010 #include <klocalizedstring.h> 0011 0012 #include "chunkdownload.h" 0013 #include "chunkselector.h" 0014 #include "version.h" 0015 #include "webseed.h" 0016 #include <diskio/chunkmanager.h> 0017 #include <diskio/piecedata.h> 0018 #include <download/piece.h> 0019 #include <interfaces/monitorinterface.h> 0020 #include <peer/accessmanager.h> 0021 #include <peer/badpeerslist.h> 0022 #include <peer/chunkcounter.h> 0023 #include <peer/peer.h> 0024 #include <peer/peerdownloader.h> 0025 #include <peer/peermanager.h> 0026 #include <torrent/torrent.h> 0027 #include <util/array.h> 0028 #include <util/error.h> 0029 #include <util/file.h> 0030 #include <util/functions.h> 0031 #include <util/log.h> 0032 #include <util/sha1hash.h> 0033 0034 namespace bt 0035 { 0036 bool Downloader::use_webseeds = true; 0037 0038 Downloader::Downloader(Torrent &tor, PeerManager &pman, ChunkManager &cman) 0039 : tor(tor) 0040 , pman(pman) 0041 , cman(cman) 0042 , bytes_downloaded(0) 0043 , tmon(nullptr) 0044 , chunk_selector(nullptr) 0045 , webseed_endgame_mode(false) 0046 { 0047 webseeds_on = use_webseeds; 0048 pman.setPieceHandler(this); 0049 chunk_selector = new ChunkSelector(); 0050 chunk_selector->init(&cman, this, &pman); 0051 0052 Uint64 total = tor.getTotalSize(); 0053 bytes_downloaded = (total - cman.bytesLeft()); 0054 curr_chunks_downloaded = 0; 0055 unnecessary_data = 0; 0056 0057 current_chunks.setAutoDelete(true); 0058 0059 active_webseed_downloads = 0; 0060 const QList<QUrl> &urls = tor.getWebSeeds(); 0061 for (const QUrl &u : urls) { 0062 if (u.scheme() == QLatin1String("http")) { 0063 WebSeed *ws = new WebSeed(u, false, tor, cman); 0064 webseeds.append(ws); 0065 connect(ws, &WebSeed::chunkReady, this, &Downloader::onChunkReady); 0066 connect(ws, &WebSeed::chunkDownloadStarted, this, &Downloader::chunkDownloadStarted); 0067 connect(ws, &WebSeed::chunkDownloadFinished, this, &Downloader::chunkDownloadFinished); 0068 } 0069 } 0070 0071 if (webseeds.count() > 0) { 0072 webseed_range_size = tor.getNumChunks() / webseeds.count(); 0073 if (webseed_range_size == 0) 0074 webseed_range_size = 1; 0075 0076 // make sure the range is not to big 0077 if (webseed_range_size > tor.getNumChunks() / 10) 0078 webseed_range_size = tor.getNumChunks() / 10; 0079 } else { 0080 webseed_range_size = 1; 0081 } 0082 } 0083 0084 Downloader::~Downloader() 0085 { 0086 delete chunk_selector; 0087 qDeleteAll(webseeds); 0088 } 0089 0090 void Downloader::setChunkSelector(ChunkSelectorInterface *csel) 0091 { 0092 delete chunk_selector; 0093 0094 if (!csel) // check if a custom one was provided, if not create a default one 0095 chunk_selector = new ChunkSelector(); 0096 else 0097 chunk_selector = csel; 0098 0099 chunk_selector->init(&cman, this, &pman); 0100 } 0101 0102 void Downloader::pieceReceived(const Piece &p) 0103 { 0104 if (cman.completed()) 0105 return; 0106 0107 ChunkDownload *cd = current_chunks.find(p.getIndex()); 0108 if (!cd) { 0109 unnecessary_data += p.getLength(); 0110 Out(SYS_DIO | LOG_DEBUG) << "Unnecessary piece, total unnecessary data : " << BytesToString(unnecessary_data) << endl; 0111 return; 0112 } 0113 0114 bool ok = false; 0115 if (cd->piece(p, ok)) { 0116 if (tmon) 0117 tmon->downloadRemoved(cd); 0118 0119 if (ok) 0120 bytes_downloaded += p.getLength(); 0121 0122 if (!finished(cd)) { 0123 // if the chunk fails don't count the bytes downloaded 0124 if (cd->getChunk()->getSize() > bytes_downloaded) 0125 bytes_downloaded = 0; 0126 else 0127 bytes_downloaded -= cd->getChunk()->getSize(); 0128 current_chunks.erase(p.getIndex()); 0129 } else { 0130 current_chunks.erase(p.getIndex()); 0131 for (WebSeed *ws : std::as_const(webseeds)) { 0132 if (ws->inCurrentRange(p.getIndex())) 0133 ws->chunkDownloaded(p.getIndex()); 0134 } 0135 } 0136 } else { 0137 if (ok) 0138 bytes_downloaded += p.getLength(); 0139 } 0140 0141 if (!ok) { 0142 unnecessary_data += p.getLength(); 0143 Out(SYS_DIO | LOG_DEBUG) << "Unnecessary piece, total unnecessary data : " << BytesToString(unnecessary_data) << endl; 0144 } 0145 } 0146 0147 bool Downloader::endgameMode() const 0148 { 0149 return current_chunks.count() >= cman.chunksLeft(); 0150 } 0151 0152 void Downloader::update() 0153 { 0154 if (cman.completed()) 0155 return; 0156 0157 /* 0158 Normal update should now handle all modes properly. 0159 */ 0160 normalUpdate(); 0161 0162 // now see if there aren't any timed out pieces 0163 for (PieceDownloader *pd : std::as_const(piece_downloaders)) { 0164 pd->checkTimeouts(); 0165 } 0166 0167 if (use_webseeds) { 0168 for (WebSeed *ws : std::as_const(webseeds)) { 0169 ws->update(); 0170 } 0171 } 0172 0173 if (isFinished() && webseeds_on) { 0174 for (WebSeed *ws : std::as_const(webseeds)) { 0175 ws->cancel(); 0176 } 0177 } 0178 } 0179 0180 void Downloader::normalUpdate() 0181 { 0182 for (CurChunkItr j = current_chunks.begin(); j != current_chunks.end(); ++j) { 0183 ChunkDownload *cd = j->second; 0184 if (cd->isIdle()) { 0185 continue; 0186 } else if (cd->isChoked()) { 0187 cd->releaseAllPDs(); 0188 } else if (cd->needsToBeUpdated()) { 0189 cd->update(); 0190 } 0191 } 0192 0193 for (PieceDownloader *pd : std::as_const(piece_downloaders)) { 0194 if (!pd->isChoked()) { 0195 while (pd->canDownloadChunk()) { 0196 if (!downloadFrom(pd)) 0197 break; 0198 pd->setNearlyDone(false); 0199 } 0200 } 0201 } 0202 0203 if (use_webseeds) { 0204 for (WebSeed *ws : std::as_const(webseeds)) { 0205 if (!ws->busy() && ws->isEnabled() && ws->failedAttempts() < 3) { 0206 downloadFrom(ws); 0207 } 0208 } 0209 } else if (webseeds_on != use_webseeds) { 0210 // reset all webseeds, webseeds have been disabled 0211 webseeds_on = use_webseeds; 0212 for (WebSeed *ws : std::as_const(webseeds)) { 0213 if (ws->busy() && ws->isEnabled()) { 0214 ws->cancel(); 0215 } 0216 } 0217 } 0218 } 0219 0220 ChunkDownload *Downloader::selectCD(PieceDownloader *pd, Uint32 n) 0221 { 0222 ChunkDownload *sel = nullptr; 0223 Uint32 sel_left = 0xFFFFFFFF; 0224 0225 for (CurChunkItr j = current_chunks.begin(); j != current_chunks.end(); ++j) { 0226 ChunkDownload *cd = j->second; 0227 if (pd->isChoked() || !pd->hasChunk(cd->getChunk()->getIndex())) 0228 continue; 0229 0230 if (cd->getNumDownloaders() == n) { 0231 // lets favor the ones which are nearly finished 0232 if (!sel || cd->getTotalPieces() - cd->getPiecesDownloaded() < sel_left) { 0233 sel = cd; 0234 sel_left = sel->getTotalPieces() - sel->getPiecesDownloaded(); 0235 } 0236 } 0237 } 0238 return sel; 0239 } 0240 0241 bool Downloader::findDownloadForPD(PieceDownloader *pd) 0242 { 0243 ChunkDownload *sel = nullptr; 0244 0245 // See if there are ChunkDownload's which need a PieceDownloader 0246 sel = selectCD(pd, 0); 0247 if (sel) { 0248 return sel->assign(pd); 0249 } 0250 0251 return false; 0252 } 0253 0254 ChunkDownload *Downloader::selectWorst(PieceDownloader *pd) 0255 { 0256 ChunkDownload *cdmin = nullptr; 0257 for (CurChunkItr j = current_chunks.begin(); j != current_chunks.end(); ++j) { 0258 ChunkDownload *cd = j->second; 0259 if (!pd->hasChunk(cd->getChunk()->getIndex()) || cd->containsPeer(pd)) 0260 continue; 0261 0262 if (!cdmin) 0263 cdmin = cd; 0264 else if (cd->getDownloadSpeed() < cdmin->getDownloadSpeed()) 0265 cdmin = cd; 0266 else if (cd->getNumDownloaders() < cdmin->getNumDownloaders()) 0267 cdmin = cd; 0268 } 0269 0270 return cdmin; 0271 } 0272 0273 bool Downloader::downloadFrom(PieceDownloader *pd) 0274 { 0275 // first see if we can use an existing dowload 0276 if (findDownloadForPD(pd)) 0277 return true; 0278 0279 Uint32 chunk = 0; 0280 if (chunk_selector->select(pd, chunk)) { 0281 Chunk *c = cman.getChunk(chunk); 0282 if (current_chunks.contains(chunk)) { 0283 return current_chunks.find(chunk)->assign(pd); 0284 } else { 0285 ChunkDownload *cd = new ChunkDownload(c); 0286 current_chunks.insert(chunk, cd); 0287 cd->assign(pd); 0288 if (tmon) 0289 tmon->downloadStarted(cd); 0290 return true; 0291 } 0292 } else if (pd->getNumGrabbed() == 0) { 0293 // If the peer hasn't got a chunk we want, 0294 ChunkDownload *cdmin = selectWorst(pd); 0295 0296 if (cdmin) { 0297 return cdmin->assign(pd); 0298 } 0299 } 0300 0301 return false; 0302 } 0303 0304 void Downloader::downloadFrom(WebSeed *ws) 0305 { 0306 Uint32 first = 0; 0307 Uint32 last = 0; 0308 webseed_endgame_mode = false; 0309 if (chunk_selector->selectRange(first, last, webseed_range_size)) { 0310 ws->download(first, last); 0311 } else { 0312 // go to endgame mode 0313 webseed_endgame_mode = true; 0314 if (chunk_selector->selectRange(first, last, webseed_range_size)) 0315 ws->download(first, last); 0316 } 0317 } 0318 0319 bool Downloader::downloading(Uint32 chunk) const 0320 { 0321 return current_chunks.find(chunk) != nullptr; 0322 } 0323 0324 bool Downloader::canDownloadFromWebSeed(Uint32 chunk) const 0325 { 0326 if (webseed_endgame_mode) 0327 return true; 0328 0329 for (WebSeed *ws : std::as_const(webseeds)) { 0330 if (ws->busy() && ws->inCurrentRange(chunk)) 0331 return false; 0332 } 0333 0334 return !downloading(chunk); 0335 } 0336 0337 Uint32 Downloader::numDownloadersForChunk(Uint32 chunk) const 0338 { 0339 const ChunkDownload *cd = current_chunks.find(chunk); 0340 if (!cd) 0341 return 0; 0342 0343 return cd->getNumDownloaders(); 0344 } 0345 0346 void Downloader::addPieceDownloader(PieceDownloader *peer) 0347 { 0348 piece_downloaders.append(peer); 0349 } 0350 0351 void Downloader::removePieceDownloader(PieceDownloader *peer) 0352 { 0353 for (CurChunkItr i = current_chunks.begin(); i != current_chunks.end(); ++i) { 0354 ChunkDownload *cd = i->second; 0355 cd->killed(peer); 0356 } 0357 piece_downloaders.removeAll(peer); 0358 } 0359 0360 bool Downloader::finished(ChunkDownload *cd) 0361 { 0362 Chunk *c = cd->getChunk(); 0363 // verify the data 0364 SHA1Hash h = cd->getHash(); 0365 0366 if (tor.verifyHash(h, c->getIndex())) { 0367 // hash ok so save it 0368 try { 0369 for (WebSeed *ws : std::as_const(webseeds)) { 0370 // tell all webseeds a chunk is downloaded 0371 if (ws->inCurrentRange(c->getIndex())) 0372 ws->chunkDownloaded(c->getIndex()); 0373 } 0374 0375 cman.chunkDownloaded(c->getIndex()); 0376 Out(SYS_GEN | LOG_IMPORTANT) << "Chunk " << c->getIndex() << " downloaded " << endl; 0377 pman.sendHave(c->getIndex()); 0378 Q_EMIT chunkDownloaded(c->getIndex()); 0379 } catch (Error &e) { 0380 Out(SYS_DIO | LOG_IMPORTANT) << "Error " << e.toString() << endl; 0381 Q_EMIT ioError(e.toString()); 0382 return false; 0383 } 0384 } else { 0385 Out(SYS_GEN | LOG_IMPORTANT) << "Hash verification error on chunk " << c->getIndex() << endl; 0386 Out(SYS_GEN | LOG_IMPORTANT) << "Is : " << h << endl; 0387 Out(SYS_GEN | LOG_IMPORTANT) << "Should be : " << tor.getHash(c->getIndex()) << endl; 0388 0389 // reset chunk but only when no webseeder is downloading it 0390 if (!webseeds_chunks.find(c->getIndex())) 0391 cman.resetChunk(c->getIndex()); 0392 0393 chunk_selector->reinsert(c->getIndex()); 0394 0395 PieceDownloader *only = cd->getOnlyDownloader(); 0396 if (only) { 0397 Peer::Ptr p = pman.findPeer(only); 0398 if (!p) 0399 return false; 0400 0401 QString ip = p->getIPAddresss(); 0402 Out(SYS_GEN | LOG_NOTICE) << "Peer " << ip << " sent bad data" << endl; 0403 AccessManager::instance().banPeer(ip); 0404 p->kill(); 0405 } 0406 return false; 0407 } 0408 return true; 0409 } 0410 0411 void Downloader::clearDownloads() 0412 { 0413 current_chunks.clear(); 0414 piece_downloaders.clear(); 0415 0416 for (WebSeed *ws : std::as_const(webseeds)) 0417 ws->cancel(); 0418 } 0419 0420 void Downloader::pause() 0421 { 0422 if (tmon) { 0423 for (CurChunkItr i = current_chunks.begin(); i != current_chunks.end(); ++i) { 0424 ChunkDownload *cd = i->second; 0425 tmon->downloadRemoved(cd); 0426 } 0427 } 0428 0429 current_chunks.clear(); 0430 for (WebSeed *ws : std::as_const(webseeds)) 0431 ws->reset(); 0432 } 0433 0434 Uint32 Downloader::downloadRate() const 0435 { 0436 // sum of the download rate of each peer 0437 Uint32 rate = 0; 0438 for (PieceDownloader *pd : std::as_const(piece_downloaders)) 0439 if (pd) 0440 rate += pd->getDownloadRate(); 0441 0442 for (WebSeed *ws : std::as_const(webseeds)) { 0443 rate += ws->getDownloadRate(); 0444 } 0445 0446 return rate; 0447 } 0448 0449 void Downloader::setMonitor(MonitorInterface *tmo) 0450 { 0451 tmon = tmo; 0452 if (!tmon) 0453 return; 0454 0455 for (CurChunkItr i = current_chunks.begin(); i != current_chunks.end(); ++i) { 0456 ChunkDownload *cd = i->second; 0457 tmon->downloadStarted(cd); 0458 } 0459 0460 for (WebSeed *ws : std::as_const(webseeds)) { 0461 WebSeedChunkDownload *cd = ws->currentChunkDownload(); 0462 if (cd) 0463 tmon->downloadStarted(cd); 0464 } 0465 } 0466 0467 void Downloader::saveDownloads(const QString &file) 0468 { 0469 File fptr; 0470 if (!fptr.open(file, "wb")) 0471 return; 0472 0473 // See bug 219019, don't know why, but it is possible that we get 0 pointers in the map 0474 // so get rid of them before we save 0475 for (CurChunkItr i = current_chunks.begin(); i != current_chunks.end();) { 0476 if (!i->second) 0477 i = current_chunks.erase(i); 0478 else 0479 ++i; 0480 } 0481 0482 // Save all the current downloads to a file 0483 CurrentChunksHeader hdr; 0484 hdr.magic = CURRENT_CHUNK_MAGIC; 0485 hdr.major = bt::MAJOR; 0486 hdr.minor = bt::MINOR; 0487 hdr.num_chunks = current_chunks.count(); 0488 fptr.write(&hdr, sizeof(CurrentChunksHeader)); 0489 0490 Out(SYS_GEN | LOG_DEBUG) << "Saving " << current_chunks.count() << " chunk downloads" << endl; 0491 for (CurChunkItr i = current_chunks.begin(); i != current_chunks.end(); ++i) { 0492 ChunkDownload *cd = i->second; 0493 cd->save(fptr); 0494 } 0495 } 0496 0497 void Downloader::loadDownloads(const QString &file) 0498 { 0499 // don't load stuff if download is finished 0500 if (cman.completed()) 0501 return; 0502 0503 // Load all partial downloads 0504 File fptr; 0505 if (!fptr.open(file, "rb")) 0506 return; 0507 0508 // recalculate downloaded bytes 0509 bytes_downloaded = (tor.getTotalSize() - cman.bytesLeft()); 0510 0511 CurrentChunksHeader chdr; 0512 fptr.read(&chdr, sizeof(CurrentChunksHeader)); 0513 if (chdr.magic != CURRENT_CHUNK_MAGIC) { 0514 Out(SYS_GEN | LOG_DEBUG) << "Warning : current_chunks file corrupted" << endl; 0515 return; 0516 } 0517 0518 Out(SYS_GEN | LOG_DEBUG) << "Loading " << chdr.num_chunks << " active chunk downloads" << endl; 0519 for (Uint32 i = 0; i < chdr.num_chunks; i++) { 0520 ChunkDownloadHeader hdr; 0521 // first read header 0522 fptr.read(&hdr, sizeof(ChunkDownloadHeader)); 0523 Out(SYS_GEN | LOG_DEBUG) << "Loading chunk " << hdr.index << endl; 0524 if (hdr.index >= tor.getNumChunks()) { 0525 Out(SYS_GEN | LOG_DEBUG) << "Warning : current_chunks file corrupted, invalid index " << hdr.index << endl; 0526 return; 0527 } 0528 0529 Chunk *c = cman.getChunk(hdr.index); 0530 if (!c || current_chunks.contains(hdr.index)) { 0531 Out(SYS_GEN | LOG_DEBUG) << "Illegal chunk " << hdr.index << endl; 0532 return; 0533 } 0534 0535 ChunkDownload *cd = new ChunkDownload(c); 0536 bool ret = false; 0537 try { 0538 ret = cd->load(fptr, hdr); 0539 } catch (...) { 0540 ret = false; 0541 } 0542 0543 if (!ret || c->getStatus() == Chunk::ON_DISK || c->isExcluded()) { 0544 delete cd; 0545 } else { 0546 current_chunks.insert(hdr.index, cd); 0547 bytes_downloaded += cd->bytesDownloaded(); 0548 0549 if (tmon) 0550 tmon->downloadStarted(cd); 0551 } 0552 } 0553 0554 // reset curr_chunks_downloaded to 0 0555 curr_chunks_downloaded = 0; 0556 } 0557 0558 Uint32 Downloader::getDownloadedBytesOfCurrentChunksFile(const QString &file) 0559 { 0560 // Load all partial downloads 0561 File fptr; 0562 if (!fptr.open(file, "rb")) 0563 return 0; 0564 0565 // read the number of chunks 0566 CurrentChunksHeader chdr; 0567 fptr.read(&chdr, sizeof(CurrentChunksHeader)); 0568 if (chdr.magic != CURRENT_CHUNK_MAGIC) { 0569 Out(SYS_GEN | LOG_DEBUG) << "Warning : current_chunks file corrupted" << endl; 0570 return 0; 0571 } 0572 Uint32 num_bytes = 0; 0573 0574 // load all chunks and calculate how much is downloaded 0575 for (Uint32 i = 0; i < chdr.num_chunks; i++) { 0576 // read the chunkdownload header 0577 ChunkDownloadHeader hdr; 0578 fptr.read(&hdr, sizeof(ChunkDownloadHeader)); 0579 0580 Chunk *c = cman.getChunk(hdr.index); 0581 if (!c) 0582 return num_bytes; 0583 0584 ChunkDownload tmp(c); 0585 if (!tmp.load(fptr, hdr, false)) 0586 return num_bytes; 0587 0588 num_bytes += tmp.bytesDownloaded(); 0589 } 0590 curr_chunks_downloaded = num_bytes; 0591 return num_bytes; 0592 } 0593 0594 bool Downloader::isFinished() const 0595 { 0596 return cman.completed(); 0597 } 0598 0599 void Downloader::onExcluded(Uint32 from, Uint32 to) 0600 { 0601 for (Uint32 i = from; i <= to; i++) { 0602 ChunkDownload *cd = current_chunks.find(i); 0603 if (!cd) 0604 continue; 0605 0606 cd->cancelAll(); 0607 cd->releaseAllPDs(); 0608 if (tmon) 0609 tmon->downloadRemoved(cd); 0610 current_chunks.erase(i); 0611 cman.resetChunk(i); // reset chunk it is not fully downloaded yet 0612 } 0613 0614 for (WebSeed *ws : std::as_const(webseeds)) { 0615 ws->onExcluded(from, to); 0616 } 0617 } 0618 0619 void Downloader::onIncluded(Uint32 from, Uint32 to) 0620 { 0621 chunk_selector->reincluded(from, to); 0622 } 0623 0624 void Downloader::corrupted(Uint32 chunk) 0625 { 0626 chunk_selector->reinsert(chunk); 0627 } 0628 0629 void Downloader::dataChecked(const bt::BitSet &ok_chunks, Uint32 from, Uint32 to) 0630 { 0631 for (Uint32 i = from; i < ok_chunks.getNumBits() && i <= to; i++) { 0632 ChunkDownload *cd = current_chunks.find(i); 0633 if (ok_chunks.get(i) && cd) { 0634 // we have a chunk and we are downloading it so kill it 0635 cd->releaseAllPDs(); 0636 if (tmon) 0637 tmon->downloadRemoved(cd); 0638 0639 current_chunks.erase(i); 0640 } 0641 } 0642 chunk_selector->dataChecked(ok_chunks, from, to); 0643 } 0644 0645 void Downloader::recalcDownloaded() 0646 { 0647 Uint64 total = tor.getTotalSize(); 0648 bytes_downloaded = (total - cman.bytesLeft()); 0649 } 0650 0651 void Downloader::onChunkReady(Chunk *c) 0652 { 0653 WebSeed *ws = webseeds_chunks.find(c->getIndex()); 0654 webseeds_chunks.erase(c->getIndex()); 0655 PieceData::Ptr piece = c->getPiece(0, c->getSize(), true); 0656 if (piece && c->checkHash(tor.getHash(c->getIndex()))) { 0657 // hash ok so save it 0658 try { 0659 bytes_downloaded += c->getSize(); 0660 0661 for (WebSeed *ws : std::as_const(webseeds)) { 0662 // tell all webseeds a chunk is downloaded 0663 if (ws->inCurrentRange(c->getIndex())) 0664 ws->chunkDownloaded(c->getIndex()); 0665 } 0666 0667 ChunkDownload *cd = current_chunks.find(c->getIndex()); 0668 if (cd) { 0669 // A ChunkDownload is ongoing for this chunk so kill it, we have the chunk 0670 cd->cancelAll(); 0671 if (tmon) 0672 tmon->downloadRemoved(cd); 0673 current_chunks.erase(c->getIndex()); 0674 } 0675 0676 c->savePiece(piece); 0677 cman.chunkDownloaded(c->getIndex()); 0678 0679 Out(SYS_GEN | LOG_IMPORTANT) << "Chunk " << c->getIndex() << " downloaded via webseed ! " << endl; 0680 // tell everybody we have the Chunk 0681 pman.sendHave(c->getIndex()); 0682 } catch (Error &e) { 0683 Out(SYS_DIO | LOG_IMPORTANT) << "Error " << e.toString() << endl; 0684 Q_EMIT ioError(e.toString()); 0685 } 0686 } else { 0687 Out(SYS_GEN | LOG_IMPORTANT) << "Hash verification error on chunk " << c->getIndex() << endl; 0688 // reset chunk but only when no other peer is downloading it 0689 if (!current_chunks.find(c->getIndex())) 0690 cman.resetChunk(c->getIndex()); 0691 0692 chunk_selector->reinsert(c->getIndex()); 0693 ws->disable(i18n("Disabled because webseed does not match torrent")); 0694 } 0695 } 0696 0697 void Downloader::chunkDownloadStarted(WebSeedChunkDownload *cd, Uint32 chunk) 0698 { 0699 webseeds_chunks.insert(chunk, cd->ws); 0700 active_webseed_downloads++; 0701 if (tmon) 0702 tmon->downloadStarted(cd); 0703 } 0704 0705 void Downloader::chunkDownloadFinished(WebSeedChunkDownload *cd, Uint32 chunk) 0706 { 0707 webseeds_chunks.erase(chunk); 0708 if (active_webseed_downloads > 0) 0709 active_webseed_downloads--; 0710 0711 if (tmon) 0712 tmon->downloadRemoved(cd); 0713 } 0714 0715 WebSeed *Downloader::addWebSeed(const QUrl &url) 0716 { 0717 // Check for dupes 0718 for (WebSeed *ws : std::as_const(webseeds)) { 0719 if (ws->getUrl() == url) 0720 return nullptr; 0721 } 0722 0723 WebSeed *ws = new WebSeed(url, true, tor, cman); 0724 webseeds.append(ws); 0725 connect(ws, &WebSeed::chunkReady, this, &Downloader::onChunkReady); 0726 connect(ws, &WebSeed::chunkDownloadStarted, this, &Downloader::chunkDownloadStarted); 0727 connect(ws, &WebSeed::chunkDownloadFinished, this, &Downloader::chunkDownloadFinished); 0728 return ws; 0729 } 0730 0731 bool Downloader::removeWebSeed(const QUrl &url) 0732 { 0733 for (WebSeed *ws : std::as_const(webseeds)) { 0734 if (ws->getUrl() == url && ws->isUserCreated()) { 0735 PtrMap<Uint32, WebSeed>::iterator i = webseeds_chunks.begin(); 0736 while (i != webseeds_chunks.end()) { 0737 if (i->second == ws) 0738 i = webseeds_chunks.erase(i); 0739 else 0740 ++i; 0741 } 0742 webseeds.removeAll(ws); 0743 delete ws; 0744 return true; 0745 } 0746 } 0747 return false; 0748 } 0749 0750 void Downloader::removeAllWebSeeds() 0751 { 0752 webseeds.clear(); 0753 webseeds_chunks.clear(); 0754 } 0755 0756 void Downloader::saveWebSeeds(const QString &file) 0757 { 0758 QFile fptr(file); 0759 if (!fptr.open(QIODevice::WriteOnly)) { 0760 Out(SYS_GEN | LOG_NOTICE) << "Cannot open " << file << " to save webseeds" << endl; 0761 return; 0762 } 0763 0764 QTextStream out(&fptr); 0765 for (WebSeed *ws : std::as_const(webseeds)) { 0766 if (ws->isUserCreated()) 0767 out << ws->getUrl().toDisplayString() << Qt::endl; 0768 } 0769 out << "====disabled====" << Qt::endl; 0770 for (WebSeed *ws : std::as_const(webseeds)) { 0771 if (!ws->isEnabled()) 0772 out << ws->getUrl().toDisplayString() << Qt::endl; 0773 } 0774 } 0775 0776 void Downloader::loadWebSeeds(const QString &file) 0777 { 0778 QFile fptr(file); 0779 if (!fptr.open(QIODevice::ReadOnly)) { 0780 Out(SYS_GEN | LOG_NOTICE) << "Cannot open " << file << " to load webseeds" << endl; 0781 return; 0782 } 0783 0784 bool disabled_list_found = false; 0785 QTextStream in(&fptr); 0786 while (!in.atEnd()) { 0787 QString line = in.readLine(); 0788 if (line == QLatin1String("====disabled====")) { 0789 disabled_list_found = true; 0790 continue; 0791 } 0792 0793 QUrl url(line); 0794 if (!url.isValid() || url.scheme() != QLatin1String("http")) 0795 continue; 0796 0797 if (disabled_list_found) { 0798 for (WebSeed *ws : std::as_const(webseeds)) { 0799 if (ws->getUrl() == url) { 0800 ws->setEnabled(false); 0801 break; 0802 } 0803 } 0804 } else { 0805 WebSeed *ws = new WebSeed(url, true, tor, cman); 0806 webseeds.append(ws); 0807 connect(ws, &WebSeed::chunkReady, this, &Downloader::onChunkReady); 0808 connect(ws, &WebSeed::chunkDownloadStarted, this, &Downloader::chunkDownloadStarted); 0809 connect(ws, &WebSeed::chunkDownloadFinished, this, &Downloader::chunkDownloadFinished); 0810 } 0811 } 0812 } 0813 0814 void Downloader::setGroupIDs(Uint32 up, Uint32 down) 0815 { 0816 for (WebSeed *ws : std::as_const(webseeds)) { 0817 ws->setGroupIDs(up, down); 0818 } 0819 } 0820 0821 ChunkDownload *Downloader::download(Uint32 chunk) 0822 { 0823 return current_chunks.find(chunk); 0824 } 0825 0826 const bt::ChunkDownload *Downloader::download(Uint32 chunk) const 0827 { 0828 return current_chunks.find(chunk); 0829 } 0830 0831 void Downloader::setUseWebSeeds(bool on) 0832 { 0833 use_webseeds = on; 0834 } 0835 } 0836 0837 #include "moc_downloader.cpp"