diff --git a/dist/windows/installer-translations/italian.nsh b/dist/windows/installer-translations/italian.nsh index a63024b01efc..2668dc5ca66b 100644 --- a/dist/windows/installer-translations/italian.nsh +++ b/dist/windows/installer-translations/italian.nsh @@ -29,7 +29,7 @@ LangString launch_qbt ${LANG_ITALIAN} "Esegui qBittorrent." ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." LangString inst_requires_64bit ${LANG_ITALIAN} "Questo installer funziona solo con versioni di Windows a 64bit." ;LangString inst_requires_win10 ${LANG_ENGLISH} "This installer requires at least Windows 10 (1809) / Windows Server 2019." -LangString inst_requires_win10 ${LANG_ITALIAN} "This installer requires at least Windows 10 (1809) / Windows Server 2019." +LangString inst_requires_win10 ${LANG_ITALIAN} "Questo installer richiede almeno Windows 10 (1809) / Windows Server 2019." ;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent" LangString inst_uninstall_link_description ${LANG_ITALIAN} "Disinstalla qBittorrent" diff --git a/src/base/bittorrent/dbresumedatastorage.cpp b/src/base/bittorrent/dbresumedatastorage.cpp index bb0efbdafc94..5e36e75fb2ea 100644 --- a/src/base/bittorrent/dbresumedatastorage.cpp +++ b/src/base/bittorrent/dbresumedatastorage.cpp @@ -67,7 +67,7 @@ namespace { const QString DB_CONNECTION_NAME = u"ResumeDataStorage"_s; - const int DB_VERSION = 7; + const int DB_VERSION = 8; const QString DB_TABLE_META = u"meta"_s; const QString DB_TABLE_TORRENTS = u"torrents"_s; @@ -628,7 +628,31 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const } if (fromVersion <= 6) - addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SHARE_LIMIT_ACTION, "TEXTNOT NULL DEFAULT `Default`"); + addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SHARE_LIMIT_ACTION, "TEXT NOT NULL DEFAULT `Default`"); + + if (fromVersion == 7) + { + const QString TEMP_COLUMN_NAME = DB_COLUMN_SHARE_LIMIT_ACTION.name + u"_temp"; + + auto queryStr = u"ALTER TABLE %1 ADD %2 %3"_s + .arg(quoted(DB_TABLE_TORRENTS), TEMP_COLUMN_NAME, u"TEXT NOT NULL DEFAULT `Default`"); + if (!query.exec(queryStr)) + throw RuntimeError(query.lastError().text()); + + queryStr = u"UPDATE %1 SET %2 = %3"_s + .arg(quoted(DB_TABLE_TORRENTS), quoted(TEMP_COLUMN_NAME), quoted(DB_COLUMN_SHARE_LIMIT_ACTION.name)); + if (!query.exec(queryStr)) + throw RuntimeError(query.lastError().text()); + + queryStr = u"ALTER TABLE %1 DROP %2"_s.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_SHARE_LIMIT_ACTION.name)); + if (!query.exec(queryStr)) + throw RuntimeError(query.lastError().text()); + + queryStr = u"ALTER TABLE %1 RENAME %2 TO %3"_s + .arg(quoted(DB_TABLE_TORRENTS), quoted(TEMP_COLUMN_NAME), quoted(DB_COLUMN_SHARE_LIMIT_ACTION.name)); + if (!query.exec(queryStr)) + throw RuntimeError(query.lastError().text()); + } const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE}); if (!query.prepare(updateMetaVersionQuery)) diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index 295b6434b94a..26ae301b4412 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -1606,7 +1606,7 @@ void SessionImpl::endStartup(ResumeSessionContext *context) reannounceToAllTrackers(); } - m_wakeupCheckTimestamp = QDateTime::currentDateTime(); + m_wakeupCheckTimestamp = now; }); m_wakeupCheckTimestamp = QDateTime::currentDateTime(); m_wakeupCheckTimer->start(30s); @@ -5526,8 +5526,6 @@ void SessionImpl::readAlerts() // Some torrents may become "finished" after different alerts handling. processPendingFinishedTorrents(); - - processTrackerStatuses(); } void SessionImpl::handleAddTorrentAlert(const lt::add_torrent_alert *alert) @@ -6203,7 +6201,10 @@ void SessionImpl::handleTrackerAlert(const lt::tracker_alert *alert) if (!torrent) return; + const auto prevSize = m_updatedTrackerStatuses.size(); QMap &updateInfo = m_updatedTrackerStatuses[torrent->nativeHandle()][std::string(alert->tracker_url())][alert->local_endpoint]; + if (prevSize < m_updatedTrackerStatuses.size()) + updateTrackerEntryStatuses(torrent->nativeHandle()); if (alert->type() == lt::tracker_reply_alert::alert_type) { @@ -6265,17 +6266,6 @@ void SessionImpl::handleTorrentConflictAlert(const lt::torrent_conflict_alert *a } #endif -void SessionImpl::processTrackerStatuses() -{ - if (m_updatedTrackerStatuses.isEmpty()) - return; - - for (auto it = m_updatedTrackerStatuses.cbegin(); it != m_updatedTrackerStatuses.cend(); ++it) - updateTrackerEntryStatuses(it.key(), it.value()); - - m_updatedTrackerStatuses.clear(); -} - void SessionImpl::saveStatistics() const { if (!m_isStatisticsDirty) @@ -6300,20 +6290,21 @@ void SessionImpl::loadStatistics() m_previouslyUploaded = value[u"AlltimeUL"_s].toLongLong(); } -void SessionImpl::updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash>> updatedTrackers) +void SessionImpl::updateTrackerEntryStatuses(lt::torrent_handle torrentHandle) { - invokeAsync([this, torrentHandle = std::move(torrentHandle), updatedTrackers = std::move(updatedTrackers)]() mutable + invokeAsync([this, torrentHandle = std::move(torrentHandle)]() mutable { try { std::vector nativeTrackers = torrentHandle.trackers(); - invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers) - , updatedTrackers = std::move(updatedTrackers)] + invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers)] { TorrentImpl *torrent = m_torrents.value(torrentHandle.info_hash()); if (!torrent || torrent->isStopped()) return; + QHash>> updatedTrackers = m_updatedTrackerStatuses.take(torrentHandle); + QHash trackers; trackers.reserve(updatedTrackers.size()); for (const lt::announce_entry &announceEntry : nativeTrackers) @@ -6363,7 +6354,7 @@ void SessionImpl::handleRemovedTorrent(const TorrentID &torrentID, const QString m_removingTorrents.erase(removingTorrentDataIter); } -QDateTime SessionImpl::fromLTTimePoint32(const libtorrent::time_point32 &timePoint) const +QDateTime SessionImpl::fromLTTimePoint32(const lt::time_point32 &timePoint) const { const auto secsSinceNow = lt::duration_cast(timePoint - m_ltNow + lt::milliseconds(500)).count(); return m_qNow.addSecs(secsSinceNow); diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index ef0c6b401693..4f35c59c2918 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -541,7 +541,6 @@ namespace BitTorrent void populateAdditionalTrackers(); void enableIPFilter(); void disableIPFilter(); - void processTrackerStatuses(); void processTorrentShareLimits(TorrentImpl *torrent); void populateExcludedFileNamesRegExpList(); void prepareStartup(); @@ -605,7 +604,7 @@ namespace BitTorrent void saveStatistics() const; void loadStatistics(); - void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash>> updatedTrackers); + void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle); void handleRemovedTorrent(const TorrentID &torrentID, const QString &partfileRemoveError = {}); diff --git a/src/base/bittorrent/torrent.h b/src/base/bittorrent/torrent.h index 2dcffdf85fd8..240075f62bb9 100644 --- a/src/base/bittorrent/torrent.h +++ b/src/base/bittorrent/torrent.h @@ -215,7 +215,15 @@ namespace BitTorrent virtual int piecesCount() const = 0; virtual int piecesHave() const = 0; virtual qreal progress() const = 0; + virtual QDateTime addedTime() const = 0; + virtual QDateTime completedTime() const = 0; + virtual QDateTime lastSeenComplete() const = 0; + virtual qlonglong activeTime() const = 0; + virtual qlonglong finishedTime() const = 0; + virtual qlonglong timeSinceUpload() const = 0; + virtual qlonglong timeSinceDownload() const = 0; + virtual qlonglong timeSinceActivity() const = 0; // Share limits virtual qreal ratioLimit() const = 0; @@ -254,8 +262,6 @@ namespace BitTorrent virtual QString error() const = 0; virtual qlonglong totalDownload() const = 0; virtual qlonglong totalUpload() const = 0; - virtual qlonglong activeTime() const = 0; - virtual qlonglong finishedTime() const = 0; virtual qlonglong eta() const = 0; virtual int seedsCount() const = 0; virtual int peersCount() const = 0; @@ -263,11 +269,6 @@ namespace BitTorrent virtual int totalSeedsCount() const = 0; virtual int totalPeersCount() const = 0; virtual int totalLeechersCount() const = 0; - virtual QDateTime lastSeenComplete() const = 0; - virtual QDateTime completedTime() const = 0; - virtual qlonglong timeSinceUpload() const = 0; - virtual qlonglong timeSinceDownload() const = 0; - virtual qlonglong timeSinceActivity() const = 0; virtual int downloadLimit() const = 0; virtual int uploadLimit() const = 0; virtual bool superSeeding() const = 0; diff --git a/src/base/bittorrent/torrentcreator.cpp b/src/base/bittorrent/torrentcreator.cpp index 3cd26ba7c03a..727e9a9dc382 100644 --- a/src/base/bittorrent/torrentcreator.cpp +++ b/src/base/bittorrent/torrentcreator.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -123,7 +124,14 @@ void TorrentCreator::run() // need to sort the file names by natural sort order QStringList dirs = {m_params.sourcePath.data()}; - QDirIterator dirIter {m_params.sourcePath.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories}; +#ifdef Q_OS_WIN + // libtorrent couldn't handle .lnk files on Windows + // Also, Windows users do not expect torrent creator to traverse into .lnk files so skip over them + const QDir::Filters dirFilters {QDir::AllDirs | QDir::NoDotAndDotDot | QDir::NoSymLinks}; +#else + const QDir::Filters dirFilters {QDir::AllDirs | QDir::NoDotAndDotDot}; +#endif + QDirIterator dirIter {m_params.sourcePath.data(), dirFilters, QDirIterator::Subdirectories}; while (dirIter.hasNext()) { const QString filePath = dirIter.next(); @@ -138,7 +146,12 @@ void TorrentCreator::run() { QStringList tmpNames; // natural sort files within each dir - QDirIterator fileIter {dir, QDir::Files}; +#ifdef Q_OS_WIN + const QDir::Filters fileFilters {QDir::Files | QDir::NoSymLinks}; +#else + const QDir::Filters fileFilters {QDir::Files}; +#endif + QDirIterator fileIter {dir, fileFilters}; while (fileIter.hasNext()) { const QFileInfo fileInfo = fileIter.nextFileInfo(); diff --git a/src/base/bittorrent/torrentdescriptor.cpp b/src/base/bittorrent/torrentdescriptor.cpp index bc5a4619a90e..072cf3e8da5d 100644 --- a/src/base/bittorrent/torrentdescriptor.cpp +++ b/src/base/bittorrent/torrentdescriptor.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015-2023 Vladimir Golovnev + * Copyright (C) 2015-2024 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -35,9 +35,7 @@ #include #include -#include #include -#include #include #include "base/global.h" @@ -147,7 +145,13 @@ BitTorrent::TorrentDescriptor::TorrentDescriptor(lt::add_torrent_params ltAddTor : m_ltAddTorrentParams {std::move(ltAddTorrentParams)} { if (m_ltAddTorrentParams.ti && m_ltAddTorrentParams.ti->is_valid()) + { m_info.emplace(*m_ltAddTorrentParams.ti); + if (m_ltAddTorrentParams.ti->creation_date() > 0) + m_creationDate = QDateTime::fromSecsSinceEpoch(m_ltAddTorrentParams.ti->creation_date()); + m_creator = QString::fromStdString(m_ltAddTorrentParams.ti->creator()); + m_comment = QString::fromStdString(m_ltAddTorrentParams.ti->comment()); + } } BitTorrent::InfoHash BitTorrent::TorrentDescriptor::infoHash() const @@ -166,18 +170,17 @@ QString BitTorrent::TorrentDescriptor::name() const QDateTime BitTorrent::TorrentDescriptor::creationDate() const { - return ((m_ltAddTorrentParams.ti->creation_date() != 0) - ? QDateTime::fromSecsSinceEpoch(m_ltAddTorrentParams.ti->creation_date()) : QDateTime()); + return m_creationDate; } QString BitTorrent::TorrentDescriptor::creator() const { - return QString::fromStdString(m_ltAddTorrentParams.ti->creator()); + return m_creator; } QString BitTorrent::TorrentDescriptor::comment() const { - return QString::fromStdString(m_ltAddTorrentParams.ti->comment()); + return m_comment; } const std::optional &BitTorrent::TorrentDescriptor::info() const diff --git a/src/base/bittorrent/torrentdescriptor.h b/src/base/bittorrent/torrentdescriptor.h index 86f9059c3508..a1c0e13dc3c3 100644 --- a/src/base/bittorrent/torrentdescriptor.h +++ b/src/base/bittorrent/torrentdescriptor.h @@ -33,7 +33,9 @@ #include #include +#include #include +#include #include "base/3rdparty/expected.hpp" #include "base/path.h" @@ -41,8 +43,6 @@ #include "torrentinfo.h" class QByteArray; -class QDateTime; -class QString; class QUrl; namespace BitTorrent @@ -78,6 +78,9 @@ namespace BitTorrent lt::add_torrent_params m_ltAddTorrentParams; std::optional m_info; + QDateTime m_creationDate; + QString m_creator; + QString m_comment; }; } diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index a3b82eff5b7a..f5a42142dcdf 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -322,6 +322,11 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession { if (m_ltAddTorrentParams.ti) { + if (const std::time_t creationDate = m_ltAddTorrentParams.ti->creation_date(); creationDate > 0) + m_creationDate = QDateTime::fromSecsSinceEpoch(creationDate); + m_creator = QString::fromStdString(m_ltAddTorrentParams.ti->creator()); + m_comment = QString::fromStdString(m_ltAddTorrentParams.ti->comment()); + // Initialize it only if torrent is added with metadata. // Otherwise it should be initialized in "Metadata received" handler. m_torrentInfo = TorrentInfo(*m_ltAddTorrentParams.ti); @@ -365,6 +370,12 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession m_urlSeeds.append(QString::fromStdString(urlSeed)); m_nativeStatus = extensionData->status; + m_addedTime = QDateTime::fromSecsSinceEpoch(m_nativeStatus.added_time); + if (m_nativeStatus.completed_time > 0) + m_completedTime = QDateTime::fromSecsSinceEpoch(m_nativeStatus.completed_time); + if (m_nativeStatus.last_seen_complete > 0) + m_lastSeenComplete = QDateTime::fromSecsSinceEpoch(m_nativeStatus.last_seen_complete); + if (hasMetadata()) updateProgress(); @@ -408,17 +419,17 @@ QString TorrentImpl::name() const QDateTime TorrentImpl::creationDate() const { - return m_torrentInfo.creationDate(); + return m_creationDate; } QString TorrentImpl::creator() const { - return m_torrentInfo.creator(); + return m_creator; } QString TorrentImpl::comment() const { - return m_torrentInfo.comment(); + return m_comment; } bool TorrentImpl::isPrivate() const @@ -947,7 +958,52 @@ void TorrentImpl::removeAllTags() QDateTime TorrentImpl::addedTime() const { - return QDateTime::fromSecsSinceEpoch(m_nativeStatus.added_time); + return m_addedTime; +} + +QDateTime TorrentImpl::completedTime() const +{ + return m_completedTime; +} + +QDateTime TorrentImpl::lastSeenComplete() const +{ + return m_lastSeenComplete; +} + +qlonglong TorrentImpl::activeTime() const +{ + return lt::total_seconds(m_nativeStatus.active_duration); +} + +qlonglong TorrentImpl::finishedTime() const +{ + return lt::total_seconds(m_nativeStatus.finished_duration); +} + +qlonglong TorrentImpl::timeSinceUpload() const +{ + if (m_nativeStatus.last_upload.time_since_epoch().count() == 0) + return -1; + + return lt::total_seconds(lt::clock_type::now() - m_nativeStatus.last_upload); +} + +qlonglong TorrentImpl::timeSinceDownload() const +{ + if (m_nativeStatus.last_download.time_since_epoch().count() == 0) + return -1; + + return lt::total_seconds(lt::clock_type::now() - m_nativeStatus.last_download); +} + +qlonglong TorrentImpl::timeSinceActivity() const +{ + const qlonglong upTime = timeSinceUpload(); + const qlonglong downTime = timeSinceDownload(); + return ((upTime < 0) != (downTime < 0)) + ? std::max(upTime, downTime) + : std::min(upTime, downTime); } qreal TorrentImpl::ratioLimit() const @@ -1266,16 +1322,6 @@ qlonglong TorrentImpl::totalUpload() const return m_nativeStatus.all_time_upload; } -qlonglong TorrentImpl::activeTime() const -{ - return lt::total_seconds(m_nativeStatus.active_duration); -} - -qlonglong TorrentImpl::finishedTime() const -{ - return lt::total_seconds(m_nativeStatus.finished_duration); -} - qlonglong TorrentImpl::eta() const { if (isStopped()) return MAX_ETA; @@ -1385,45 +1431,6 @@ int TorrentImpl::totalLeechersCount() const return (m_nativeStatus.num_incomplete > -1) ? m_nativeStatus.num_incomplete : (m_nativeStatus.list_peers - m_nativeStatus.list_seeds); } -QDateTime TorrentImpl::lastSeenComplete() const -{ - if (m_nativeStatus.last_seen_complete > 0) - return QDateTime::fromSecsSinceEpoch(m_nativeStatus.last_seen_complete); - else - return {}; -} - -QDateTime TorrentImpl::completedTime() const -{ - if (m_nativeStatus.completed_time > 0) - return QDateTime::fromSecsSinceEpoch(m_nativeStatus.completed_time); - else - return {}; -} - -qlonglong TorrentImpl::timeSinceUpload() const -{ - if (m_nativeStatus.last_upload.time_since_epoch().count() == 0) - return -1; - return lt::total_seconds(lt::clock_type::now() - m_nativeStatus.last_upload); -} - -qlonglong TorrentImpl::timeSinceDownload() const -{ - if (m_nativeStatus.last_download.time_since_epoch().count() == 0) - return -1; - return lt::total_seconds(lt::clock_type::now() - m_nativeStatus.last_download); -} - -qlonglong TorrentImpl::timeSinceActivity() const -{ - const qlonglong upTime = timeSinceUpload(); - const qlonglong downTime = timeSinceDownload(); - return ((upTime < 0) != (downTime < 0)) - ? std::max(upTime, downTime) - : std::min(upTime, downTime); -} - int TorrentImpl::downloadLimit() const { return m_downloadLimit;; @@ -2625,11 +2632,23 @@ bool TorrentImpl::isMoveInProgress() const void TorrentImpl::updateStatus(const lt::torrent_status &nativeStatus) { + // Since libtorrent alerts are handled asynchronously there can be obsolete + // "state update" event reached here after torrent was reloaded in libtorrent. + // Just discard such events. + if (nativeStatus.handle != m_nativeHandle) [[unlikely]] + return; + const lt::torrent_status oldStatus = std::exchange(m_nativeStatus, nativeStatus); if (m_nativeStatus.num_pieces != oldStatus.num_pieces) updateProgress(); + if (m_nativeStatus.completed_time != oldStatus.completed_time) + m_completedTime = (m_nativeStatus.completed_time > 0) ? QDateTime::fromSecsSinceEpoch(m_nativeStatus.completed_time) : QDateTime(); + + if (m_nativeStatus.last_seen_complete != oldStatus.last_seen_complete) + m_lastSeenComplete = QDateTime::fromSecsSinceEpoch(m_nativeStatus.last_seen_complete); + updateState(); m_payloadRateMonitor.addSample({nativeStatus.download_payload_rate diff --git a/src/base/bittorrent/torrentimpl.h b/src/base/bittorrent/torrentimpl.h index ffc83b202fa2..f968642c7010 100644 --- a/src/base/bittorrent/torrentimpl.h +++ b/src/base/bittorrent/torrentimpl.h @@ -138,7 +138,15 @@ namespace BitTorrent int piecesCount() const override; int piecesHave() const override; qreal progress() const override; + QDateTime addedTime() const override; + QDateTime completedTime() const override; + QDateTime lastSeenComplete() const override; + qlonglong activeTime() const override; + qlonglong finishedTime() const override; + qlonglong timeSinceUpload() const override; + qlonglong timeSinceDownload() const override; + qlonglong timeSinceActivity() const override; qreal ratioLimit() const override; void setRatioLimit(qreal limit) override; @@ -181,8 +189,6 @@ namespace BitTorrent QString error() const override; qlonglong totalDownload() const override; qlonglong totalUpload() const override; - qlonglong activeTime() const override; - qlonglong finishedTime() const override; qlonglong eta() const override; QVector filesProgress() const override; int seedsCount() const override; @@ -191,11 +197,6 @@ namespace BitTorrent int totalSeedsCount() const override; int totalPeersCount() const override; int totalLeechersCount() const override; - QDateTime lastSeenComplete() const override; - QDateTime completedTime() const override; - qlonglong timeSinceUpload() const override; - qlonglong timeSinceDownload() const override; - qlonglong timeSinceActivity() const override; int downloadLimit() const override; int uploadLimit() const override; bool superSeeding() const override; @@ -342,6 +343,14 @@ namespace BitTorrent InfoHash m_infoHash; + QDateTime m_creationDate; + QString m_creator; + QString m_comment; + + QDateTime m_addedTime; + QDateTime m_completedTime; + QDateTime m_lastSeenComplete; + // m_moveFinishedTriggers is activated only when the following conditions are met: // all file rename jobs complete, all file move jobs complete QQueue m_moveFinishedTriggers; diff --git a/src/base/rss/rss_autodownloadrule.cpp b/src/base/rss/rss_autodownloadrule.cpp index 3a8265c23e1e..2960b984bdf4 100644 --- a/src/base/rss/rss_autodownloadrule.cpp +++ b/src/base/rss/rss_autodownloadrule.cpp @@ -396,6 +396,8 @@ bool AutoDownloadRule::matchesSmartEpisodeFilter(const QString &articleTitle) co m_dataPtr->lastComputedEpisodes.append(episodeStr + u"-REPACK"); m_dataPtr->lastComputedEpisodes.append(episodeStr + u"-PROPER"); } + + return true; } m_dataPtr->lastComputedEpisodes.append(episodeStr); diff --git a/src/base/rss/rss_parser.cpp b/src/base/rss/rss_parser.cpp index 7c9acf4ca200..f1cb2b65db07 100644 --- a/src/base/rss/rss_parser.cpp +++ b/src/base/rss/rss_parser.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2015-2024 Vladimir Golovnev * Copyright (C) 2012 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -29,11 +29,8 @@ #include "rss_parser.h" -#include #include -#include #include -#include #include #include #include @@ -359,7 +356,7 @@ namespace }; // Ported to Qt from KDElibs4 - QDateTime parseDate(const QString &string) + QDateTime parseDate(const QString &string, const QDateTime &fallbackDate) { const char16_t shortDay[][4] = { @@ -382,7 +379,7 @@ namespace const QString str = string.trimmed(); if (str.isEmpty()) - return QDateTime::currentDateTime(); + return fallbackDate; int nyear = 6; // indexes within string to values int nmonth = 4; @@ -402,14 +399,14 @@ namespace const bool h1 = (parts[3] == u"-"); const bool h2 = (parts[5] == u"-"); if (h1 != h2) - return QDateTime::currentDateTime(); + return fallbackDate; } else { // Check for the obsolete form "Wdy Mon DD HH:MM:SS YYYY" rx = QRegularExpression {u"^([A-Z][a-z]+)\\s+(\\S+)\\s+(\\d\\d)\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\s+(\\d\\d\\d\\d)$"_s}; if (str.indexOf(rx, 0, &rxMatch) != 0) - return QDateTime::currentDateTime(); + return fallbackDate; nyear = 7; nmonth = 2; @@ -427,14 +424,14 @@ namespace const int hour = parts[nhour].toInt(&ok[2]); const int minute = parts[nmin].toInt(&ok[3]); if (!ok[0] || !ok[1] || !ok[2] || !ok[3]) - return QDateTime::currentDateTime(); + return fallbackDate; int second = 0; if (!parts[nsec].isEmpty()) { second = parts[nsec].toInt(&ok[0]); if (!ok[0]) - return QDateTime::currentDateTime(); + return fallbackDate; } const bool leapSecond = (second == 60); @@ -518,21 +515,21 @@ namespace const QDate qDate(year, month + 1, day); // convert date, and check for out-of-range if (!qDate.isValid()) - return QDateTime::currentDateTime(); + return fallbackDate; const QTime qTime(hour, minute, second); QDateTime result(qDate, qTime, Qt::UTC); if (offset) result = result.addSecs(-offset); if (!result.isValid()) - return QDateTime::currentDateTime(); // invalid date/time + return fallbackDate; // invalid date/time if (leapSecond) { // Validate a leap second time. Leap seconds are inserted after 23:59:59 UTC. // Convert the time to UTC and check that it is 00:00:00. if ((hour*3600 + minute*60 + 60 - offset + 86400*5) % 86400) // (max abs(offset) is 100 hours) - return QDateTime::currentDateTime(); // the time isn't the last second of the day + return fallbackDate; // the time isn't the last second of the day } return result; @@ -550,6 +547,7 @@ RSS::Private::Parser::Parser(const QString &lastBuildDate) void RSS::Private::Parser::parse(const QByteArray &feedData) { QXmlStreamReader xml {feedData}; + m_fallbackDate = QDateTime::currentDateTime(); XmlStreamEntityResolver resolver; xml.setEntityResolver(&resolver); bool foundChannel = false; @@ -641,7 +639,7 @@ void RSS::Private::Parser::parseRssArticle(QXmlStreamReader &xml) } else if (name == u"pubDate") { - article[Article::KeyDate] = parseDate(xml.readElementText().trimmed()); + article[Article::KeyDate] = parseDate(xml.readElementText().trimmed(), m_fallbackDate); } else if (name == u"author") { @@ -755,7 +753,7 @@ void RSS::Private::Parser::parseAtomArticle(QXmlStreamReader &xml) { // ATOM uses standard compliant date, don't do fancy stuff const QDateTime articleDate = QDateTime::fromString(xml.readElementText().trimmed(), Qt::ISODate); - article[Article::KeyDate] = (articleDate.isValid() ? articleDate : QDateTime::currentDateTime()); + article[Article::KeyDate] = (articleDate.isValid() ? articleDate : m_fallbackDate); } else if (name == u"author") { diff --git a/src/base/rss/rss_parser.h b/src/base/rss/rss_parser.h index 7abfeeb2edf8..17d4a2464225 100644 --- a/src/base/rss/rss_parser.h +++ b/src/base/rss/rss_parser.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2015-2024 Vladimir Golovnev * Copyright (C) 2012 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -29,6 +29,7 @@ #pragma once +#include #include #include #include @@ -66,6 +67,7 @@ namespace RSS::Private void parseAtomChannel(QXmlStreamReader &xml); void addArticle(QVariantHash article); + QDateTime m_fallbackDate; QString m_baseUrl; ParsingResult m_result; QSet m_articleIDs; diff --git a/src/gui/rss/htmlbrowser.cpp b/src/gui/rss/htmlbrowser.cpp index ecf3ff7adad3..4c34923bcbc8 100644 --- a/src/gui/rss/htmlbrowser.cpp +++ b/src/gui/rss/htmlbrowser.cpp @@ -106,10 +106,12 @@ void HtmlBrowser::resourceLoaded(QNetworkReply *reply) atts[QNetworkRequest::HttpStatusCodeAttribute] = 200; atts[QNetworkRequest::HttpReasonPhraseAttribute] = u"Ok"_s; metaData.setAttributes(atts); - metaData.setLastModified(QDateTime::currentDateTime()); - metaData.setExpirationDate(QDateTime::currentDateTime().addDays(1)); + const auto currentDateTime = QDateTime::currentDateTime(); + metaData.setLastModified(currentDateTime); + metaData.setExpirationDate(currentDateTime.addDays(1)); QIODevice *dev = m_diskCache->prepare(metaData); - if (!dev) return; + if (!dev) + return; QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning).pixmap(32, 32).save(dev, "PNG"); m_diskCache->insert(dev); diff --git a/src/webui/api/serialize/serialize_torrent.cpp b/src/webui/api/serialize/serialize_torrent.cpp index d48a3a5c8db4..b589fc05b81e 100644 --- a/src/webui/api/serialize/serialize_torrent.cpp +++ b/src/webui/api/serialize/serialize_torrent.cpp @@ -104,7 +104,7 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent) const qlonglong timeSinceActivity = torrent.timeSinceActivity(); return (timeSinceActivity < 0) ? Utils::DateTime::toSecsSinceEpoch(torrent.addedTime()) - : (QDateTime::currentDateTime().toSecsSinceEpoch() - timeSinceActivity); + : (QDateTime::currentSecsSinceEpoch() - timeSinceActivity); }; return { diff --git a/src/webui/api/torrentcreatorcontroller.cpp b/src/webui/api/torrentcreatorcontroller.cpp index ced5ec390c4e..8167ec99fb52 100644 --- a/src/webui/api/torrentcreatorcontroller.cpp +++ b/src/webui/api/torrentcreatorcontroller.cpp @@ -129,7 +129,7 @@ void TorrentCreatorController::addTaskAction() .sourcePath = Path(params()[KEY_SOURCE_PATH]), .torrentFilePath = Path(params()[KEY_TORRENT_FILE_PATH]), .comment = params()[KEY_COMMENT], - .source = params()[KEY_COMMENT], + .source = params()[KEY_SOURCE], .trackers = params()[KEY_TRACKERS].split(u'|'), .urlSeeds = params()[KEY_URL_SEEDS].split(u'|') }; diff --git a/src/webui/www/private/scripts/prop-trackers.js b/src/webui/www/private/scripts/prop-trackers.js index f6c746580bec..5ca1291660df 100644 --- a/src/webui/www/private/scripts/prop-trackers.js +++ b/src/webui/www/private/scripts/prop-trackers.js @@ -219,7 +219,7 @@ window.qBittorrent.PropTrackers = (function() { method: "post", data: { hash: current_hash, - urls: selectedTrackers.join("|") + urls: selectedTrackers.map(encodeURIComponent).join("|") }, onSuccess: function() { updateData(); diff --git a/src/webui/www/public/scripts/login.js b/src/webui/www/public/scripts/login.js index 598e076998dc..5950aaa3bff2 100644 --- a/src/webui/www/public/scripts/login.js +++ b/src/webui/www/public/scripts/login.js @@ -37,10 +37,13 @@ function submitLoginForm(event) { xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8"); xhr.addEventListener("readystatechange", () => { if (xhr.readyState === 4) { // DONE state - if ((xhr.status === 200) && (xhr.responseText === "Ok.")) + if ((xhr.status === 200) && (xhr.responseText === "Ok.")) { location.replace(location); - else + location.reload(true); + } + else { errorMsgElement.textContent = "QBT_TR(Invalid Username or Password.)QBT_TR[CONTEXT=HttpServer]"; + } } }); xhr.addEventListener("error", () => {