Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport changes to v5.0.x branch #21496

Open
wants to merge 8 commits into
base: v5_0_x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci_windows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ jobs:
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: "6.7.0"
version: "6.7.3"
archives: qtbase qtsvg qttools
cache: true

Expand Down
11 changes: 1 addition & 10 deletions src/app/main.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014-2023 Vladimir Golovnev <[email protected]>
* Copyright (C) 2014-2024 Vladimir Golovnev <[email protected]>
* Copyright (C) 2006 Christophe Dumez <[email protected]>
*
* This program is free software; you can redistribute it and/or
Expand Down Expand Up @@ -58,10 +58,6 @@
#include <QSplashScreen>
#include <QTimer>

#ifdef Q_OS_WIN
#include <QOperatingSystemVersion>
#endif

#ifdef QBT_STATIC_QT
#include <QtPlugin>
Q_IMPORT_PLUGIN(QICOPlugin)
Expand Down Expand Up @@ -189,11 +185,6 @@ int main(int argc, char *argv[])
// We must save it here because QApplication constructor may change it
const bool isOneArg = (argc == 2);

#if !defined(DISABLE_GUI) && defined(Q_OS_WIN)
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10)
QApplication::setStyle(u"Fusion"_s);
#endif

// `app` must be declared out of try block to allow display message box in case of exception
std::unique_ptr<Application> app;
try
Expand Down
57 changes: 29 additions & 28 deletions src/base/addtorrentmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,36 @@ void AddTorrentManager::handleAddTorrentFailed(const QString &source, const QStr
emit addTorrentFailed(source, reason);
}

void AddTorrentManager::handleDuplicateTorrent(const QString &source, BitTorrent::Torrent *torrent, const QString &message)
void AddTorrentManager::handleDuplicateTorrent(const QString &source
, const BitTorrent::TorrentDescriptor &torrentDescr, BitTorrent::Torrent *existingTorrent)
{
const bool hasMetadata = torrentDescr.info().has_value();
if (hasMetadata)
{
// Trying to set metadata to existing torrent in case if it has none
existingTorrent->setMetadata(*torrentDescr.info());
}

const bool isPrivate = existingTorrent->isPrivate() || (hasMetadata && torrentDescr.info()->isPrivate());
QString message;
if (!btSession()->isMergeTrackersEnabled())
{
message = tr("Merging of trackers is disabled");
}
else if (isPrivate)
{
message = tr("Trackers cannot be merged because it is a private torrent");
}
else
{
// merge trackers and web seeds
existingTorrent->addTrackers(torrentDescr.trackers());
existingTorrent->addUrlSeeds(torrentDescr.urlSeeds());
message = tr("Trackers are merged from new source");
}

LogMsg(tr("Detected an attempt to add a duplicate torrent. Source: %1. Existing torrent: %2. Result: %3")
.arg(source, torrent->name(), message));
.arg(source, existingTorrent->name(), message));
emit addTorrentFailed(source, message);
}

Expand All @@ -184,32 +210,7 @@ bool AddTorrentManager::processTorrent(const QString &source, const BitTorrent::
if (BitTorrent::Torrent *torrent = btSession()->findTorrent(infoHash))
{
// a duplicate torrent is being added

const bool hasMetadata = torrentDescr.info().has_value();
if (hasMetadata)
{
// Trying to set metadata to existing torrent in case if it has none
torrent->setMetadata(*torrentDescr.info());
}

if (!btSession()->isMergeTrackersEnabled())
{
handleDuplicateTorrent(source, torrent, tr("Merging of trackers is disabled"));
return false;
}

const bool isPrivate = torrent->isPrivate() || (hasMetadata && torrentDescr.info()->isPrivate());
if (isPrivate)
{
handleDuplicateTorrent(source, torrent, tr("Trackers cannot be merged because it is a private torrent"));
return false;
}

// merge trackers and web seeds
torrent->addTrackers(torrentDescr.trackers());
torrent->addUrlSeeds(torrentDescr.urlSeeds());

handleDuplicateTorrent(source, torrent, tr("Trackers are merged from new source"));
handleDuplicateTorrent(source, torrentDescr, torrent);
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion src/base/addtorrentmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class AddTorrentManager : public ApplicationComponent<QObject>
bool addTorrentToSession(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr
, const BitTorrent::AddTorrentParams &addTorrentParams);
void handleAddTorrentFailed(const QString &source, const QString &reason);
void handleDuplicateTorrent(const QString &source, BitTorrent::Torrent *torrent, const QString &message);
void handleDuplicateTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr, BitTorrent::Torrent *existingTorrent);
void setTorrentFileGuard(const QString &source, std::shared_ptr<TorrentFileGuard> torrentFileGuard);
void releaseTorrentFileGuard(const QString &source);

Expand Down
54 changes: 31 additions & 23 deletions src/base/bittorrent/sessionimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ SessionImpl::SessionImpl(QObject *parent)
, m_I2POutboundQuantity {BITTORRENT_SESSION_KEY(u"I2P/OutboundQuantity"_s), 3}
, m_I2PInboundLength {BITTORRENT_SESSION_KEY(u"I2P/InboundLength"_s), 3}
, m_I2POutboundLength {BITTORRENT_SESSION_KEY(u"I2P/OutboundLength"_s), 3}
, m_torrentContentRemoveOption {BITTORRENT_SESSION_KEY(u"TorrentContentRemoveOption"_s), TorrentContentRemoveOption::MoveToTrash}
, m_torrentContentRemoveOption {BITTORRENT_SESSION_KEY(u"TorrentContentRemoveOption"_s), TorrentContentRemoveOption::Delete}
, m_startPaused {BITTORRENT_SESSION_KEY(u"StartPaused"_s)}
, m_seedingLimitTimer {new QTimer(this)}
, m_resumeDataTimer {new QTimer(this)}
Expand Down Expand Up @@ -5206,6 +5206,9 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
if (torrent)
{
torrent->handleMoveStorageJobFinished(newPath, finishedJob.context, torrentHasOutstandingJob);
// The torrent may become "finished" at the end of the move if it was moved
// from the "incomplete" location after downloading finished.
processPendingFinishedTorrents();
}
else if (!torrentHasOutstandingJob)
{
Expand All @@ -5217,6 +5220,32 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
}
}

void SessionImpl::processPendingFinishedTorrents()
{
if (m_pendingFinishedTorrents.isEmpty())
return;

for (TorrentImpl *torrent : asConst(m_pendingFinishedTorrents))
{
LogMsg(tr("Torrent download finished. Torrent: \"%1\"").arg(torrent->name()));
emit torrentFinished(torrent);

if (const Path exportPath = finishedTorrentExportDirectory(); !exportPath.isEmpty())
exportTorrentFile(torrent, exportPath);

processTorrentShareLimits(torrent);
}

m_pendingFinishedTorrents.clear();

const bool hasUnfinishedTorrents = std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentImpl *torrent)
{
return !(torrent->isFinished() || torrent->isStopped() || torrent->isErrored());
});
if (!hasUnfinishedTorrents)
emit allTorrentsFinished();
}

void SessionImpl::storeCategories() const
{
QJsonObject jsonObj;
Expand Down Expand Up @@ -6108,28 +6137,7 @@ void SessionImpl::handleStateUpdateAlert(const lt::state_update_alert *alert)
if (!updatedTorrents.isEmpty())
emit torrentsUpdated(updatedTorrents);

if (!m_pendingFinishedTorrents.isEmpty())
{
for (TorrentImpl *torrent : m_pendingFinishedTorrents)
{
LogMsg(tr("Torrent download finished. Torrent: \"%1\"").arg(torrent->name()));
emit torrentFinished(torrent);

if (const Path exportPath = finishedTorrentExportDirectory(); !exportPath.isEmpty())
exportTorrentFile(torrent, exportPath);

processTorrentShareLimits(torrent);
}

m_pendingFinishedTorrents.clear();

const bool hasUnfinishedTorrents = std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentImpl *torrent)
{
return !(torrent->isFinished() || torrent->isStopped() || torrent->isErrored());
});
if (!hasUnfinishedTorrents)
emit allTorrentsFinished();
}
processPendingFinishedTorrents();

if (m_needSaveTorrentsQueue)
saveTorrentsQueue();
Expand Down
1 change: 1 addition & 0 deletions src/base/bittorrent/sessionimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ namespace BitTorrent

void moveTorrentStorage(const MoveStorageJob &job) const;
void handleMoveTorrentStorageJobFinished(const Path &newPath);
void processPendingFinishedTorrents();

void loadCategories();
void storeCategories() const;
Expand Down
13 changes: 13 additions & 0 deletions src/base/preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,19 @@ void Preferences::setWinStartup(const bool b)
settings.remove(profileID);
}
}

QString Preferences::getStyle() const
{
return value<QString>(u"Appearance/Style"_s);
}

void Preferences::setStyle(const QString &styleName)
{
if (styleName == getStyle())
return;

setValue(u"Appearance/Style"_s, styleName);
}
#endif // Q_OS_WIN

// Downloads
Expand Down
2 changes: 2 additions & 0 deletions src/base/preferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ class Preferences final : public QObject
#ifdef Q_OS_WIN
bool WinStartup() const;
void setWinStartup(bool b);
QString getStyle() const;
void setStyle(const QString &styleName);
#endif

// Downloads
Expand Down
5 changes: 5 additions & 0 deletions src/base/utils/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ Path Utils::OS::windowsSystemPath()
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
bool Utils::OS::applyMarkOfTheWeb(const Path &file, const QString &url)
{
// Trying to apply this to a non-existent file is unacceptable,
// as it may unexpectedly create such a file.
if (!file.exists())
return false;

Q_ASSERT(url.isEmpty() || url.startsWith(u"http:") || url.startsWith(u"https:"));

#ifdef Q_OS_MACOS
Expand Down
50 changes: 29 additions & 21 deletions src/gui/guiaddtorrentmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,40 +175,48 @@ void GUIAddTorrentManager::onMetadataDownloaded(const BitTorrent::TorrentInfo &m
}
}

bool GUIAddTorrentManager::processTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr, const BitTorrent::AddTorrentParams &params)
bool GUIAddTorrentManager::processTorrent(const QString &source
, const BitTorrent::TorrentDescriptor &torrentDescr, const BitTorrent::AddTorrentParams &params)
{
const bool hasMetadata = torrentDescr.info().has_value();
const BitTorrent::InfoHash infoHash = torrentDescr.infoHash();

// Prevent showing the dialog if download is already present
if (BitTorrent::Torrent *torrent = btSession()->findTorrent(infoHash))
{
if (hasMetadata)
if (Preferences::instance()->confirmMergeTrackers())
{
// Trying to set metadata to existing torrent in case if it has none
torrent->setMetadata(*torrentDescr.info());
}

if (torrent->isPrivate() || (hasMetadata && torrentDescr.info()->isPrivate()))
{
handleDuplicateTorrent(source, torrent, tr("Trackers cannot be merged because it is a private torrent"));
}
else
{
bool mergeTrackers = btSession()->isMergeTrackersEnabled();
if (Preferences::instance()->confirmMergeTrackers())
if (hasMetadata)
{
const QMessageBox::StandardButton btn = RaisedMessageBox::question(app()->mainWindow(), tr("Torrent is already present")
, tr("Torrent '%1' is already in the transfer list. Do you want to merge trackers from new source?").arg(torrent->name())
, (QMessageBox::Yes | QMessageBox::No), QMessageBox::Yes);
mergeTrackers = (btn == QMessageBox::Yes);
// Trying to set metadata to existing torrent in case if it has none
torrent->setMetadata(*torrentDescr.info());
}

if (mergeTrackers)
const bool isPrivate = torrent->isPrivate() || (hasMetadata && torrentDescr.info()->isPrivate());
const QString dialogCaption = tr("Torrent is already present");
if (isPrivate)
{
torrent->addTrackers(torrentDescr.trackers());
torrent->addUrlSeeds(torrentDescr.urlSeeds());
// We cannot merge trackers for private torrent but we still notify user
// about duplicate torrent if confirmation dialog is enabled.
RaisedMessageBox::warning(app()->mainWindow(), dialogCaption
, tr("Trackers cannot be merged because it is a private torrent."));
}
else
{
const bool mergeTrackers = btSession()->isMergeTrackersEnabled();
const QMessageBox::StandardButton btn = RaisedMessageBox::question(app()->mainWindow(), dialogCaption
, tr("Torrent '%1' is already in the transfer list. Do you want to merge trackers from new source?").arg(torrent->name())
, (QMessageBox::Yes | QMessageBox::No), (mergeTrackers ? QMessageBox::Yes : QMessageBox::No));
if (btn == QMessageBox::Yes)
{
torrent->addTrackers(torrentDescr.trackers());
torrent->addUrlSeeds(torrentDescr.urlSeeds());
}
}
}
else
{
handleDuplicateTorrent(source, torrentDescr, torrent);
}

return false;
Expand Down
Loading
Loading