File indexing completed on 2024-04-28 04:57:30

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2007-2008 Lukas Appelhans <l.appelhans@gmx.de>
0004    Copyright (C) 2007 Joris Guisson   <joris.guisson@gmail.com>
0005 
0006    This program is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 */
0011 
0012 #include "bttransfer.h"
0013 #include "bittorrentsettings.h"
0014 #include "bttransferhandler.h"
0015 // #include "btchunkselector.h"
0016 #include "advanceddetails/monitor.h"
0017 #include "core/download.h"
0018 #include "core/filemodel.h"
0019 #include "core/kget.h"
0020 #include "kget_version.h"
0021 
0022 #include <interfaces/trackerinterface.h>
0023 #include <peer/authenticationmonitor.h>
0024 #include <peer/peermanager.h>
0025 #include <torrent/globals.h>
0026 #include <torrent/server.h>
0027 #include <torrent/torrent.h>
0028 #include <util/constants.h>
0029 #include <util/error.h>
0030 #include <util/functions.h>
0031 #include <util/log.h>
0032 #include <utp/utpserver.h>
0033 #include <version.h>
0034 
0035 #include "kget_debug.h"
0036 #include <KIO/CopyJob>
0037 #include <KLocalizedString>
0038 
0039 #include <KMessageBox>
0040 
0041 #include <QDir>
0042 #include <QDomElement>
0043 #include <QFile>
0044 #include <QFileDialog>
0045 #include <QFileInfo>
0046 #include <QStandardPaths>
0047 #include <QUrl>
0048 
0049 #ifdef ERROR
0050 #undef ERROR
0051 #endif
0052 
0053 BTTransfer::BTTransfer(TransferGroup *parent, TransferFactory *factory, Scheduler *scheduler, const QUrl &src, const QUrl &dest, const QDomElement *e)
0054     : Transfer(parent, factory, scheduler, src, dest, e)
0055     , torrent(nullptr)
0056     , m_tmp(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/tmp/"))
0057     , m_ready(false)
0058     , m_downloadFinished(false)
0059     , m_movingFile(false)
0060     , m_fileModel(nullptr)
0061     , m_updateCounter(0)
0062 {
0063     QString tmpDirName = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/tmp/");
0064     // make sure that the /tmp directory exists (earlier this used to be handled by KStandardDirs)
0065     if (!QFileInfo::exists(tmpDirName)) {
0066         QDir().mkpath(tmpDirName);
0067     }
0068     m_directory = KIO::upUrl(m_dest); // FIXME test
0069 
0070     setCapabilities(Transfer::Cap_Moving | Transfer::Cap_Renaming | Transfer::Cap_Resuming | Transfer::Cap_SpeedLimit);
0071 }
0072 
0073 BTTransfer::~BTTransfer()
0074 {
0075     if (torrent && m_ready)
0076         torrent->setMonitor(nullptr);
0077 
0078     delete torrent;
0079 }
0080 
0081 void BTTransfer::deinit(Transfer::DeleteOptions options)
0082 {
0083     qDebug() << "****************************DEINIT";
0084     if (torrent && (options & Transfer::DeleteFiles)) { // FIXME: Also delete when torrent does not exist
0085         torrent->deleteDataFiles();
0086     }
0087     if (options & Transfer::DeleteTemporaryFiles) {
0088         QDir tmpDir(m_tmp);
0089         qCDebug(KGET_DEBUG) << m_tmp + m_source.fileName().remove(".torrent");
0090         tmpDir.rmdir(m_source.fileName().remove(".torrent") + "/dnd");
0091         tmpDir.cd(m_source.fileName().remove(".torrent"));
0092         QStringList list = tmpDir.entryList();
0093         foreach (const QString &file, list) {
0094             tmpDir.remove(file);
0095         }
0096         tmpDir.cdUp();
0097         tmpDir.rmdir(m_source.fileName().remove(".torrent"));
0098 
0099         // only remove the .torrent file if it was downloaded by KGet
0100         if (!m_tmpTorrentFile.isEmpty()) {
0101             qCDebug(KGET_DEBUG) << "Removing" << m_tmpTorrentFile;
0102             QFile torrentFile(m_tmpTorrentFile);
0103             torrentFile.remove();
0104         }
0105     }
0106 }
0107 
0108 /** Reimplemented functions from Transfer-Class **/
0109 bool BTTransfer::isStalled() const
0110 {
0111     return (status() == Job::Running) && (downloadSpeed() == 0) && torrent && torrent->getStats().status == bt::STALLED;
0112 }
0113 
0114 bool BTTransfer::isWorking() const
0115 {
0116     if (!torrent)
0117         return false;
0118     const bt::TorrentStats stats = torrent->getStats();
0119     return (stats.status != bt::ERROR) && (stats.status != bt::STALLED) && (stats.status != bt::NO_SPACE_LEFT) && (stats.status != bt::INVALID_STATUS);
0120 }
0121 
0122 void BTTransfer::resolveError(int errorId)
0123 {
0124     switch (errorId) {
0125     case TorrentFileNotFoundError: {
0126         QFileDialog *dlg = new QFileDialog(nullptr, i18nc("@title", "Select a New Torrent File"));
0127         dlg->setFileMode(QFileDialog::ExistingFile);
0128         dlg->setMimeTypeFilters(QStringList{QStringLiteral("application/x-bittorrent")});
0129         dlg->setAttribute(Qt::WA_DeleteOnClose);
0130         connect(dlg, &QDialog::accepted, this, [this, dlg] {
0131             QUrl url = dlg->selectedUrls().value(0);
0132             if (url.isValid()) {
0133                 btTransferInit(url);
0134             }
0135         });
0136         dlg->show();
0137 
0138         break;
0139     }
0140     default:
0141         return;
0142     }
0143 }
0144 
0145 void BTTransfer::start()
0146 {
0147     if (m_movingFile) {
0148         return;
0149     }
0150 
0151     if (!torrent) {
0152         if (!m_source.isLocalFile()) {
0153             qCDebug(KGET_DEBUG) << m_dest.path();
0154             QString tmpDirName = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/tmp/");
0155             m_tmpTorrentFile = tmpDirName + m_dest.fileName();
0156             auto *download = new Download(m_source, QUrl::fromLocalFile(m_tmpTorrentFile));
0157 
0158             setStatus(Job::Stopped, i18n("Downloading Torrent File...."), "document-save");
0159             setTransferChange(Tc_Status, true);
0160 
0161             // m_source = tmpDirName + m_source.fileName();
0162             connect(download, &Download::finishedSuccessfully, this, &BTTransfer::btTransferInit);
0163         } else
0164             btTransferInit();
0165     } else
0166         startTorrent();
0167 }
0168 
0169 bool BTTransfer::setDirectory(const QUrl &newDirectory)
0170 {
0171     // check if the newDestination is the same as the old
0172     QUrl temp = newDirectory;
0173     temp = temp.adjusted(QUrl::StripTrailingSlash);
0174     temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name));
0175     if (newDirectory.isValid() && (newDirectory != dest()) && (temp != dest())) {
0176         if (torrent->changeOutputDir(newDirectory.url(QUrl::PreferLocalFile), bt::TorrentInterface::MOVE_FILES)) {
0177             connect(torrent, &bt::TorrentInterface::aboutToBeStarted, this, &BTTransfer::newDestResult);
0178             m_movingFile = true;
0179             m_directory = newDirectory;
0180             m_dest = m_directory;
0181             m_dest = m_dest.adjusted(QUrl::StripTrailingSlash);
0182             m_dest.setPath(m_dest.path() + '/' + (torrent->getStats().torrent_name));
0183 
0184             setStatus(Job::Stopped, i18nc("changing the destination of the file", "Changing destination"), "media-playback-pause");
0185             setTransferChange(Tc_Status, true);
0186             return true;
0187         }
0188     }
0189     m_movingFile = false;
0190     return false;
0191 }
0192 
0193 void BTTransfer::newDestResult()
0194 {
0195     disconnect(torrent, &bt::TorrentInterface::aboutToBeStarted, this, &BTTransfer::newDestResult);
0196     m_movingFile = false;
0197 
0198     setStatus(Job::Running, i18nc("transfer state: downloading", "Downloading...."), "media-playback-start");
0199     setTransferChange(Tc_FileName | Tc_Status, true);
0200 }
0201 
0202 void BTTransfer::stop()
0203 {
0204     if (m_movingFile)
0205         return;
0206 
0207     if (m_ready) {
0208         stopTorrent();
0209     }
0210 }
0211 
0212 /**Own public functions**/
0213 void BTTransfer::update()
0214 {
0215     if (m_movingFile) {
0216         return;
0217     }
0218 
0219     if (torrent) {
0220         QStringList files;
0221         if (torrent->hasMissingFiles(files)) {
0222             torrent->recreateMissingFiles();
0223         }
0224         updateTorrent();
0225     } else
0226         timer.stop();
0227 }
0228 
0229 void BTTransfer::load(const QDomElement *element)
0230 {
0231     Transfer::load(element);
0232 
0233     if ((m_totalSize == m_downloadedSize) && (m_totalSize != 0)) {
0234         setStatus(Job::Stopped, i18nc("transfer state: finished", "Finished"), "dialog-ok");
0235     }
0236 }
0237 
0238 // void BTTransfer::save(const QDomElement &element)
0239 // {
0240 //     qCDebug(KGET_DEBUG);
0241 //
0242 //     QDomElement e = element;
0243 //
0244 //     Transfer::save(e);
0245 // }
0246 
0247 /**Public functions of BTTransfer**/
0248 
0249 void BTTransfer::setPort(int port)
0250 {
0251     bt::Globals::instance().getTCPServer().changePort(port);
0252     if (BittorrentSettings::enableUTP())
0253         bt::Globals::instance().getUTPServer().changePort(port + 1);
0254 }
0255 
0256 void BTTransfer::setSpeedLimits(int ulLimit, int dlLimit)
0257 {
0258     qCDebug(KGET_DEBUG);
0259     if (!torrent)
0260         return;
0261 
0262     torrent->setTrafficLimits(ulLimit * 1000, dlLimit * 1000);
0263 }
0264 
0265 void BTTransfer::addTracker(const QString &url)
0266 {
0267     qCDebug(KGET_DEBUG);
0268     if (torrent->getStats().priv_torrent) {
0269         KMessageBox::error(nullptr, i18n("Cannot add a tracker to a private torrent."));
0270         return;
0271     }
0272 
0273     QUrl u(url);
0274     if (!u.isValid()) {
0275         KMessageBox::error(nullptr, i18n("Malformed URL."));
0276         return;
0277     }
0278 
0279     torrent->getTrackersList()->addTracker(u, true);
0280 }
0281 
0282 /**Private functions**/
0283 
0284 void BTTransfer::startTorrent()
0285 {
0286     if (m_ready) {
0287         // qCDebug(KGET_DEBUG) << "Going to download that stuff :-0";
0288         setSpeedLimits(uploadLimit(Transfer::InvisibleSpeedLimit), downloadLimit(Transfer::InvisibleSpeedLimit)); // Set traffic-limits before starting
0289         torrent->setMonitor(this);
0290         torrent->start();
0291         timer.start(250);
0292         if (chunksTotal() == chunksDownloaded() /* && !m_downloadFinished*/) {
0293             slotDownloadFinished(torrent);
0294         } else {
0295             setStatus(Job::Running, i18nc("transfer state: downloading", "Downloading...."), "media-playback-start");
0296         }
0297         m_totalSize = torrent->getStats().total_bytes_to_download;
0298         setTransferChange(Tc_Status | Tc_TrackersList | Tc_TotalSize, true);
0299         updateFilesStatus();
0300     }
0301 }
0302 
0303 void BTTransfer::stopTorrent()
0304 {
0305     torrent->stop();
0306     torrent->setMonitor(nullptr);
0307     m_downloadSpeed = 0;
0308     timer.stop();
0309 
0310     if (m_downloadFinished) {
0311         setStatus(Job::Stopped, i18nc("transfer state: finished", "Finished"), "dialog-ok");
0312     } else {
0313         setStatus(Job::Stopped, i18nc("transfer state: stopped", "Stopped"), "process-stop");
0314     }
0315     setTransferChange(Tc_Status, true);
0316 
0317     updateFilesStatus();
0318 }
0319 
0320 void BTTransfer::updateTorrent()
0321 {
0322     // qCDebug(KGET_DEBUG) << "Update torrent";
0323     bt::UpdateCurrentTime();
0324     bt::AuthenticationMonitor::instance().update();
0325     torrent->update();
0326 
0327     ChangesFlags changesFlags = 0;
0328 
0329     if (m_downloadedSize != (m_downloadedSize = torrent->getStats().bytes_downloaded))
0330         changesFlags |= Tc_DownloadedSize;
0331 
0332     if (m_uploadSpeed != static_cast<int>(torrent->getStats().upload_rate)) {
0333         m_uploadSpeed = torrent->getStats().upload_rate;
0334         changesFlags |= Tc_UploadSpeed;
0335     }
0336 
0337     if (m_downloadSpeed != static_cast<int>(torrent->getStats().download_rate)) {
0338         m_downloadSpeed = torrent->getStats().download_rate;
0339         changesFlags |= Tc_DownloadSpeed;
0340     }
0341 
0342     int percent = (chunksDownloaded() * 100) / chunksTotal();
0343     if (m_percent != percent) {
0344         m_percent = percent;
0345         changesFlags |= Tc_Percent;
0346     }
0347 
0348     setTransferChange(changesFlags, true);
0349 
0350     // update the files status every 3 seconds
0351     if (!m_updateCounter) {
0352         updateFilesStatus();
0353         m_updateCounter = 12;
0354     }
0355     --m_updateCounter;
0356 }
0357 
0358 void BTTransfer::updateFilesStatus()
0359 {
0360     const Job::Status currentStatus = this->status();
0361     if (!torrent) {
0362         return;
0363     }
0364     const bt::TorrentStats *stats = &torrent->getStats();
0365     if (stats->multi_file_torrent) {
0366         QHash<QUrl, bt::TorrentFileInterface *>::const_iterator it;
0367         QHash<QUrl, bt::TorrentFileInterface *>::const_iterator itEnd = m_files.constEnd();
0368         for (it = m_files.constBegin(); it != itEnd; ++it) {
0369             QModelIndex status = m_fileModel->index(it.key(), FileItem::Status);
0370             if (!(*it)->doNotDownload() && (currentStatus == Job::Running)) {
0371                 m_fileModel->setData(status, Job::Running);
0372             } else {
0373                 m_fileModel->setData(status, Job::Stopped);
0374             }
0375             if (qFuzzyCompare((*it)->getDownloadPercentage(), 100.0f)) {
0376                 m_fileModel->setData(status, Job::Finished);
0377             }
0378         }
0379     } else {
0380         QModelIndexList indexes = fileModel()->fileIndexes(FileItem::Status);
0381         if (indexes.count() != 1) {
0382             return;
0383         }
0384 
0385         QModelIndex index = indexes.first();
0386         if (stats->bytes_left_to_download) {
0387             if (currentStatus == Job::Running) {
0388                 fileModel()->setData(index, Job::Running);
0389             } else {
0390                 fileModel()->setData(index, Job::Stopped);
0391             }
0392         } else {
0393             fileModel()->setData(index, Job::Finished);
0394         }
0395     }
0396 }
0397 
0398 void BTTransfer::btTransferInit(const QUrl &src, const QByteArray &data)
0399 {
0400     Q_UNUSED(data)
0401     qCDebug(KGET_DEBUG);
0402     if (src != m_source && !src.isEmpty())
0403         m_source = src;
0404 
0405     QFile file(m_source.toLocalFile());
0406 
0407     if (!file.open(QIODevice::ReadOnly)) {
0408         setError(i18n("Torrent file does not exist"), "dialog-cancel", Job::NotSolveable, TorrentFileNotFoundError);
0409         setTransferChange(Tc_Status, true);
0410         resolveError(TorrentFileNotFoundError);
0411         return;
0412     }
0413 
0414     setStatus(Job::Stopped,
0415               i18n("Analyzing torrent...."),
0416               "document-preview"); // jpetso says: you should probably use the "process-working" icon here (from the animations category), but that's a
0417                                    // multi-frame PNG so it's hard for me to test
0418     setTransferChange(Tc_Status, true);
0419 
0420     bt::InitLog(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/torrentlog.log"),
0421                 false,
0422                 false); // initialize the torrent-log
0423 
0424     bt::SetClientInfo("KGet", KGET_VERSION_MAJOR, KGET_VERSION_MINOR, KGET_VERSION_PATCH, bt::NORMAL, "KG"); // Set client info to KGet
0425 
0426     bt::Uint16 i = 0;
0427     while (!bt::Globals::instance().initTCPServer(BittorrentSettings::port() + i) && i < 10)
0428         i++;
0429 
0430     if (i == 10) {
0431         setError(i18n("Cannot initialize port..."), "dialog-cancel");
0432         setTransferChange(Tc_Status);
0433         return;
0434     }
0435     if (BittorrentSettings::enableUTP()) {
0436         while (!bt::Globals::instance().initUTPServer(BittorrentSettings::port() + i) && i < 10) // We don't care if it fails for now as UTP is experimental...
0437             i++;
0438     }
0439 
0440     QDir tmpDir(m_tmp + m_source.fileName().remove(".torrent"));
0441     if (tmpDir.exists()) {
0442         tmpDir.remove("torrent");
0443     }
0444     try {
0445         torrent = new bt::TorrentControl();
0446 
0447         if (!BittorrentSettings::tmpDir().isEmpty() && QFileInfo(BittorrentSettings::tmpDir()).isDir()) {
0448             m_tmp = BittorrentSettings::tmpDir();
0449         }
0450 
0451         m_ready = true;
0452 
0453         qDebug() << "Source:" << m_source.path() << "Destination:" << m_dest.path();
0454         m_dest = m_dest.adjusted(QUrl::StripTrailingSlash);
0455         torrent->init(nullptr,
0456                       file.readAll(),
0457                       m_tmp + m_source.fileName().remove(".torrent"),
0458                       QUrl::fromLocalFile(m_dest.adjusted(QUrl::RemoveFilename).path()).toLocalFile());
0459 
0460         m_dest = QUrl::fromLocalFile(torrent->getStats().output_path);
0461         if (!torrent->getStats().multi_file_torrent
0462             && (m_dest.fileName() != torrent->getStats().torrent_name)) // TODO check if this is needed, so if that case is true at some point
0463         {
0464             m_dest = m_dest.adjusted(QUrl::StripTrailingSlash);
0465             m_dest.setPath(m_dest.path() + '/' + (torrent->getStats().torrent_name));
0466         }
0467 
0468         torrent->createFiles();
0469 
0470         torrent->setPreallocateDiskSpace(BittorrentSettings::preAlloc());
0471 
0472         connect(torrent, SIGNAL(stoppedByError(bt::TorrentInterface *, QString)), SLOT(slotStoppedByError(bt::TorrentInterface *, QString)));
0473         connect(torrent, &bt::TorrentInterface::finished, this, &BTTransfer::slotDownloadFinished);
0474         // FIXME connect(tc,SIGNAL(corruptedDataFound(bt::TorrentInterface*)), this, SLOT(emitCorruptedData(bt::TorrentInterface*)));//TODO: Fix it
0475     } catch (bt::Error &err) {
0476         m_ready = false;
0477         torrent->deleteLater();
0478         torrent = nullptr;
0479         setError(err.toString(), "dialog-cancel", Job::NotSolveable);
0480         setTransferChange(Tc_Status);
0481         return;
0482     }
0483     startTorrent();
0484     connect(&timer, &QTimer::timeout, this, &BTTransfer::update);
0485 }
0486 
0487 void BTTransfer::slotStoppedByError(const bt::TorrentInterface *&error, const QString &errormsg)
0488 {
0489     Q_UNUSED(error)
0490     stop();
0491     setError(errormsg, "dialog-cancel", Job::NotSolveable);
0492     setTransferChange(Tc_Status);
0493 }
0494 
0495 void BTTransfer::slotDownloadFinished(bt::TorrentInterface *ti)
0496 {
0497     qCDebug(KGET_DEBUG) << "Start seeding *********************************************************************";
0498     Q_UNUSED(ti)
0499     m_downloadFinished = true;
0500     // timer.stop();
0501     setStatus(Job::FinishedKeepAlive, i18nc("Transfer status: seeding", "Seeding...."), "media-playback-start");
0502     setTransferChange(Tc_Status, true);
0503 }
0504 
0505 /**Property-Functions**/
0506 QList<QUrl> BTTransfer::trackersList() const
0507 {
0508     if (!torrent)
0509         return QList<QUrl>();
0510 
0511     QList<QUrl> trackers;
0512     foreach (bt::TrackerInterface *tracker, torrent->getTrackersList()->getTrackers())
0513         trackers << tracker->trackerURL();
0514     return trackers;
0515 }
0516 
0517 int BTTransfer::sessionBytesDownloaded() const
0518 {
0519     if (!torrent)
0520         return -1;
0521 
0522     return torrent->getStats().session_bytes_downloaded;
0523 }
0524 
0525 int BTTransfer::sessionBytesUploaded() const
0526 {
0527     if (!torrent)
0528         return -1;
0529 
0530     return torrent->getStats().session_bytes_uploaded;
0531 }
0532 
0533 int BTTransfer::chunksTotal() const
0534 {
0535     if (!torrent)
0536         return -1;
0537 
0538     return torrent->getTorrent().getNumChunks();
0539 }
0540 
0541 int BTTransfer::chunksDownloaded() const
0542 {
0543     if (!torrent)
0544         return -1;
0545 
0546     return torrent->downloadedChunksBitSet().numOnBits();
0547 }
0548 
0549 int BTTransfer::chunksExcluded() const
0550 {
0551     if (!torrent)
0552         return -1;
0553 
0554     return torrent->excludedChunksBitSet().numOnBits();
0555 }
0556 
0557 int BTTransfer::chunksLeft() const
0558 {
0559     if (!torrent)
0560         return -1;
0561 
0562     return chunksTotal() - chunksDownloaded();
0563 }
0564 
0565 int BTTransfer::seedsConnected() const
0566 {
0567     if (!torrent)
0568         return -1;
0569 
0570     return torrent->getStats().seeders_connected_to;
0571 }
0572 
0573 int BTTransfer::seedsDisconnected() const
0574 {
0575     if (!torrent)
0576         return -1;
0577 
0578     return torrent->getStats().seeders_total;
0579 }
0580 
0581 int BTTransfer::leechesConnected() const
0582 {
0583     if (!torrent)
0584         return -1;
0585 
0586     return torrent->getStats().leechers_connected_to;
0587 }
0588 
0589 int BTTransfer::leechesDisconnected() const
0590 {
0591     if (!torrent)
0592         return -1;
0593 
0594     return torrent->getStats().leechers_total;
0595 }
0596 
0597 int BTTransfer::elapsedTime() const
0598 {
0599     if (!torrent)
0600         return -1;
0601 
0602     return torrent->getRunningTimeDL();
0603 }
0604 
0605 int BTTransfer::remainingTime() const
0606 {
0607     if (!torrent)
0608         return Transfer::remainingTime();
0609 
0610     return torrent->getETA();
0611 }
0612 
0613 bt::TorrentControl *BTTransfer::torrentControl()
0614 {
0615     return torrent;
0616 }
0617 
0618 bool BTTransfer::ready()
0619 {
0620     return m_ready;
0621 }
0622 
0623 void BTTransfer::downloadRemoved(bt::ChunkDownloadInterface *cd)
0624 {
0625     if (static_cast<BTTransferHandler *>(handler())->torrentMonitor())
0626         static_cast<BTTransferHandler *>(handler())->torrentMonitor()->downloadRemoved(cd);
0627 
0628     setTransferChange(Tc_ChunksDownloaded | Tc_ChunksExcluded | Tc_ChunksLeft, true);
0629 }
0630 
0631 void BTTransfer::downloadStarted(bt::ChunkDownloadInterface *cd)
0632 {
0633     if (static_cast<BTTransferHandler *>(handler())->torrentMonitor())
0634         static_cast<BTTransferHandler *>(handler())->torrentMonitor()->downloadStarted(cd);
0635 
0636     setTransferChange(Tc_ChunksDownloaded | Tc_ChunksExcluded | Tc_ChunksLeft, true);
0637 }
0638 
0639 void BTTransfer::peerAdded(bt::PeerInterface *peer)
0640 {
0641     if (static_cast<BTTransferHandler *>(handler())->torrentMonitor())
0642         static_cast<BTTransferHandler *>(handler())->torrentMonitor()->peerAdded(peer);
0643 
0644     setTransferChange(Tc_SeedsConnected | Tc_SeedsDisconnected | Tc_LeechesConnected | Tc_LeechesDisconnected, true);
0645 }
0646 
0647 void BTTransfer::peerRemoved(bt::PeerInterface *peer)
0648 {
0649     if (static_cast<BTTransferHandler *>(handler())->torrentMonitor())
0650         static_cast<BTTransferHandler *>(handler())->torrentMonitor()->peerRemoved(peer);
0651 
0652     setTransferChange(Tc_SeedsConnected | Tc_SeedsDisconnected | Tc_LeechesConnected | Tc_LeechesDisconnected, true);
0653 }
0654 
0655 void BTTransfer::stopped()
0656 {
0657     if (static_cast<BTTransferHandler *>(handler())->torrentMonitor())
0658         static_cast<BTTransferHandler *>(handler())->torrentMonitor()->stopped();
0659 }
0660 
0661 void BTTransfer::destroyed()
0662 {
0663     if (static_cast<BTTransferHandler *>(handler())->torrentMonitor())
0664         static_cast<BTTransferHandler *>(handler())->torrentMonitor()->destroyed();
0665 }
0666 
0667 QList<QUrl> BTTransfer::files() const
0668 {
0669     QList<QUrl> urls;
0670 
0671     if (!torrent) {
0672         return urls;
0673     }
0674 
0675     // multiple files
0676     if (torrent->getStats().multi_file_torrent) {
0677         for (uint i = 0; i < torrent->getNumFiles(); ++i) {
0678             const QString path = torrent->getTorrentFile(i).getPathOnDisk();
0679             urls.append(QUrl(path));
0680         }
0681     }
0682     // one single file
0683     else {
0684         QUrl temp = m_dest;
0685         if (m_dest.fileName() != torrent->getStats().torrent_name) // TODO check if the body is ever entered!
0686         {
0687             temp = temp.adjusted(QUrl::StripTrailingSlash);
0688             temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name));
0689         }
0690         urls.append(temp);
0691     }
0692 
0693     return urls;
0694 }
0695 
0696 void BTTransfer::filesSelected()
0697 {
0698     QModelIndexList indexes = fileModel()->fileIndexes(FileItem::File);
0699     // one single file
0700     if (indexes.count() == 1) {
0701         QModelIndex index = indexes.first();
0702         const bool doDownload = index.data(Qt::CheckStateRole).toBool();
0703         if (torrent && torrent->getStats().bytes_left_to_download) {
0704             if (doDownload) {
0705                 start();
0706             } else {
0707                 stop();
0708             }
0709         }
0710     }
0711     // multiple files
0712     else {
0713         foreach (const QModelIndex &index, indexes) {
0714             const QUrl dest = fileModel()->getUrl(index);
0715             const bool doDownload = index.data(Qt::CheckStateRole).toBool();
0716             bt::TorrentFileInterface *file = m_files[dest];
0717             file->setDoNotDownload(!doDownload);
0718         }
0719     }
0720 
0721     //     setTransferChange(Tc_TotalSize | Tc_DownloadedSize | Tc_Percent, true);
0722 }
0723 
0724 FileModel *BTTransfer::fileModel() // TODO correct file model for one-file-torrents
0725 {
0726     if (!m_fileModel) {
0727         if (!torrent) {
0728             return nullptr;
0729         }
0730 
0731         // multiple files
0732         if (torrent->getStats().multi_file_torrent) {
0733             for (bt::Uint32 i = 0; i < torrent->getNumFiles(); ++i) {
0734                 bt::TorrentFileInterface *file = &torrent->getTorrentFile(i);
0735                 m_files[QUrl(file->getPathOnDisk())] = file;
0736             }
0737             m_fileModel = new FileModel(m_files.keys(), directory(), this);
0738             //         connect(m_fileModel, SIGNAL(rename(QUrl,QUrl)), this, SLOT(slotRename(QUrl,QUrl)));
0739             connect(m_fileModel, &FileModel::checkStateChanged, this, &BTTransfer::filesSelected);
0740 
0741             // set the checkstate, the status and the size of the model items
0742             QHash<QUrl, bt::TorrentFileInterface *>::const_iterator it;
0743             QHash<QUrl, bt::TorrentFileInterface *>::const_iterator itEnd = m_files.constEnd();
0744             const Job::Status curentStatus = this->status();
0745             for (it = m_files.constBegin(); it != itEnd; ++it) {
0746                 QModelIndex size = m_fileModel->index(it.key(), FileItem::Size);
0747                 m_fileModel->setData(size, static_cast<qlonglong>((*it)->getSize()));
0748 
0749                 const bool doDownload = !(*it)->doNotDownload();
0750                 QModelIndex checkIndex = m_fileModel->index(it.key(), FileItem::File);
0751                 const Qt::CheckState checkState = doDownload ? Qt::Checked : Qt::Unchecked;
0752                 m_fileModel->setData(checkIndex, checkState, Qt::CheckStateRole);
0753 
0754                 QModelIndex status = m_fileModel->index(it.key(), FileItem::Status);
0755                 if (doDownload && (curentStatus == Job::Running)) {
0756                     m_fileModel->setData(status, Job::Running);
0757                 } else {
0758                     m_fileModel->setData(status, Job::Stopped);
0759                 }
0760                 if (qFuzzyCompare((*it)->getDownloadPercentage(), 100.0f)) {
0761                     m_fileModel->setData(status, Job::Finished);
0762                 }
0763             }
0764         }
0765         // one single file
0766         else {
0767             QList<QUrl> urls;
0768             QUrl temp = m_dest;
0769             if (m_dest.fileName() != torrent->getStats().torrent_name) // TODO check if the body is ever entered!
0770             {
0771                 temp = temp.adjusted(QUrl::StripTrailingSlash);
0772                 temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name));
0773             }
0774             const QUrl url = temp;
0775             urls.append(url);
0776 
0777             m_fileModel = new FileModel(urls, directory(), this);
0778             //         connect(m_fileModel, SIGNAL(rename(QUrl,QUrl)), this, SLOT(slotRename(QUrl,QUrl)));
0779             connect(m_fileModel, &FileModel::checkStateChanged, this, &BTTransfer::filesSelected);
0780 
0781             QModelIndex size = m_fileModel->index(url, FileItem::Size);
0782             m_fileModel->setData(size, static_cast<qlonglong>(torrent->getStats().total_bytes));
0783 
0784             QModelIndex checkIndex = m_fileModel->index(url, FileItem::File);
0785             m_fileModel->setData(checkIndex, Qt::Checked, Qt::CheckStateRole);
0786 
0787             QModelIndex status = m_fileModel->index(url, FileItem::Status);
0788             if (this->status() == Job::Running) {
0789                 m_fileModel->setData(status, Job::Running);
0790             } else {
0791                 m_fileModel->setData(status, Job::Stopped);
0792             }
0793             if (!torrent->getStats().bytes_left_to_download) {
0794                 m_fileModel->setData(status, Job::Finished);
0795             }
0796         }
0797     }
0798 
0799     return m_fileModel;
0800 }
0801 
0802 #include "moc_bttransfer.cpp"