You've already forked qBittorrent
mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-10-09 18:32:15 +02:00
Compare commits
75 Commits
release-4.
...
release-4.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
359b464958 | ||
![]() |
b5b0d68dd9 | ||
![]() |
f48c49c248 | ||
![]() |
11ca744548 | ||
![]() |
82e6fc700e | ||
![]() |
59fd70c638 | ||
![]() |
617bf767df | ||
![]() |
55180e3598 | ||
![]() |
56b62e6573 | ||
![]() |
b37e7b0340 | ||
![]() |
21aebaf16f | ||
![]() |
5792465317 | ||
![]() |
e98f44af63 | ||
![]() |
40cf0203fb | ||
![]() |
05a82afeb6 | ||
![]() |
a456f1b0f9 | ||
![]() |
4acc44a5b0 | ||
![]() |
9c2a1146df | ||
![]() |
807abeae87 | ||
![]() |
dd2a0d0484 | ||
![]() |
3f3400f43b | ||
![]() |
334b57a89a | ||
![]() |
00d6c83ee5 | ||
![]() |
e8850c7a70 | ||
![]() |
2ef96eb218 | ||
![]() |
4682e31ab7 | ||
![]() |
988f7e2ef8 | ||
![]() |
6007913291 | ||
![]() |
cdcc7a210b | ||
![]() |
a466ff5057 | ||
![]() |
e954835579 | ||
![]() |
3e9be3a0e8 | ||
![]() |
4ab32a76f6 | ||
![]() |
bad60058df | ||
![]() |
31a6ad1eb6 | ||
![]() |
a8bfec081e | ||
![]() |
ae21d0f1e2 | ||
![]() |
c599976b6f | ||
![]() |
bcee784097 | ||
![]() |
697fc626cd | ||
![]() |
2f15ea9b54 | ||
![]() |
d03209a73d | ||
![]() |
ac9ba255d8 | ||
![]() |
9a7e79bd0e | ||
![]() |
e8be3bf939 | ||
![]() |
74e52746b1 | ||
![]() |
8d26a221e0 | ||
![]() |
3fdab88eb7 | ||
![]() |
d376d912b3 | ||
![]() |
e329c41ef2 | ||
![]() |
01e4179555 | ||
![]() |
06f503b5df | ||
![]() |
e2f3dad7b8 | ||
![]() |
377f31085c | ||
![]() |
ec13d195f8 | ||
![]() |
c01aed8d90 | ||
![]() |
ad7b8a9bfa | ||
![]() |
5bba1ed208 | ||
![]() |
fe94e14bcc | ||
![]() |
b0af479ab9 | ||
![]() |
24ff369f29 | ||
![]() |
979c9a7094 | ||
![]() |
7b90ac52c1 | ||
![]() |
ecfbda78bc | ||
![]() |
9ba7470815 | ||
![]() |
6394467cc7 | ||
![]() |
f6d72fa79f | ||
![]() |
32ed5f1c8e | ||
![]() |
5026da5773 | ||
![]() |
ef130e4438 | ||
![]() |
4fbd52c2d5 | ||
![]() |
8f29b70c1e | ||
![]() |
9a4dd3ea9d | ||
![]() |
fcd3bb6918 | ||
![]() |
9f69fd8750 |
43
Changelog
43
Changelog
@@ -1,3 +1,46 @@
|
||||
* Mon Sep 23 2019 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.8
|
||||
- BUGFIX: Fix filename validation on non-Windows OS (Chocobo1)
|
||||
- BUGFIX: ScanFolders/FileSystemWatcher now detect magnet files with case insensitivity in filename (Chocobo1)
|
||||
- BUGFIX: Fix failed seeding after creating a torrent and auto-adding it to the session (Chocobo1)
|
||||
|
||||
* Sun Aug 04 2019 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.7
|
||||
- FEATURE: Add 12 hour and 24 hour speed graphs (dzmat)
|
||||
- FEATURE: Change "Add new torrent" dialog to horizontal layout (Evgeny Lensky)
|
||||
- BUGFIX: Fix messed up symbols in log (Chocobo1)
|
||||
- BUGFIX: Fix incomplete file extension not applied for new torrents (Chocobo1)
|
||||
- BUGFIX: Save updated resume data for completed torrents (Vladimir Golovnev (Glassez))
|
||||
- BUGFIX: Fix requested torrent resume data handling (Vladimir Golovnev (Glassez))
|
||||
- BUGFIX: Prevent command injection via "Run external program" function (Chocobo1)
|
||||
- BUGFIX: Avoid race conditions when adding torrent (Vladimir Golovnev (Glassez))
|
||||
- BUGFIX: Fix torrent checking issues (Vladimir Golovnev (Glassez))
|
||||
- BUGFIX: Use proper log message when there are no error (Chocobo1)
|
||||
- BUGFIX: Fix torrent properties not saved for paused torrents (Chocobo1)
|
||||
- BUGFIX: Some improvements on qtsingleapplication code (Chocobo1)
|
||||
- BUGFIX: Remove limits of "Disk cache expiry interval" setting (Chocobo1)
|
||||
- BUGFIX: Remove upper limit of "Disk cache" setting (Chocobo1)
|
||||
- BUGFIX: Fix crash when removing phantom tags (Chocobo1)
|
||||
- BUGFIX: Improve handleFileErrorAlert error message (Chocobo1)
|
||||
- BUGFIX: Fix updated save path not saved for paused torrents (Chocobo1)
|
||||
- BUGFIX: Log save_resume_data_failed_alert (Chocobo1)
|
||||
- BUGFIX: Don't remove parent directories (Chocobo1)
|
||||
- BUGFIX: Properly remove empty leftover folders after rename (Chocobo1)
|
||||
- BUGFIX: Focus behavior row in Options dialog (silverqx)
|
||||
- BUGFIX: Fix unable to rename folder on Windows when same is used in different case(Chocobo1)
|
||||
- BUGFIX: Fix unable to control add torrent dialogs when opened simultaneously (Chocobo1)
|
||||
- BUGFIX: Disable "Upload mode" when start preloaded torrent (Vladimir Golovnev (Glassez))
|
||||
- BUGFIX: Fix wrong comparison result when sorting items(Chocobo1)
|
||||
- BUGFIX: Fix sequential downloading when redirected (Vladimir Golovnev (Glassez))
|
||||
- BUGFIX: Fix typos (Chocobo1)
|
||||
- BUGFIX: Fix assertion fail (Chocobo1)
|
||||
- BUGFIX: Change number of time axis divisions from 5 to 6 for convenience (dzmat)
|
||||
- BUGFIX: Don't turn window blank when closed to system tray (Ekin Dursun)
|
||||
- WEBUI: Fix WebUI encoding of special characters (Thomas Piccirello)
|
||||
- WEBUI: Change the speed unit from Bytes/s to KiB/s for the rate limiter(jerrymakesjelly)
|
||||
- WEBUI: Fix '+' char not decoded to space correctly (Chocobo1)
|
||||
- RSS: Ignore RSS articles with non-unique identifiers (Vladimir Golovnev (Glassez))
|
||||
- RSS: Perform more RSS parsing in working thread (Vladimir Golovnev (Glassez))
|
||||
- RSS: Download RSS enclosure element if no proper MIME type is found (Matan Bareket)
|
||||
|
||||
* Sun May 05 2019 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.6
|
||||
- BUGFIX: Force recheck multiple torrents one by one in all possible cases. Closes #9120 (glassez)
|
||||
- BUGFIX: Don't query Google for tracker favicons, for privacy reasons (sledgehammer999)
|
||||
|
24
configure
vendored
24
configure
vendored
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for qbittorrent v4.1.6.
|
||||
# Generated by GNU Autoconf 2.69 for qbittorrent v4.1.8.
|
||||
#
|
||||
# Report bugs to <bugs.qbittorrent.org>.
|
||||
#
|
||||
@@ -580,8 +580,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='qbittorrent'
|
||||
PACKAGE_TARNAME='qbittorrent'
|
||||
PACKAGE_VERSION='v4.1.6'
|
||||
PACKAGE_STRING='qbittorrent v4.1.6'
|
||||
PACKAGE_VERSION='v4.1.8'
|
||||
PACKAGE_STRING='qbittorrent v4.1.8'
|
||||
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
||||
PACKAGE_URL='https://www.qbittorrent.org/'
|
||||
|
||||
@@ -1299,7 +1299,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures qbittorrent v4.1.6 to adapt to many kinds of systems.
|
||||
\`configure' configures qbittorrent v4.1.8 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1370,7 +1370,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of qbittorrent v4.1.6:";;
|
||||
short | recursive ) echo "Configuration of qbittorrent v4.1.8:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1505,7 +1505,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
qbittorrent configure v4.1.6
|
||||
qbittorrent configure v4.1.8
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -1644,7 +1644,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by qbittorrent $as_me v4.1.6, which was
|
||||
It was created by qbittorrent $as_me v4.1.8, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -3822,7 +3822,7 @@ fi
|
||||
|
||||
# Define the identity of the package.
|
||||
PACKAGE='qbittorrent'
|
||||
VERSION='v4.1.6'
|
||||
VERSION='v4.1.8'
|
||||
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
@@ -6274,7 +6274,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by qbittorrent $as_me v4.1.6, which was
|
||||
This file was extended by qbittorrent $as_me v4.1.8, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -6332,7 +6332,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
qbittorrent config.status v4.1.6
|
||||
qbittorrent config.status v4.1.8
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
@@ -7590,7 +7590,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by qbittorrent $as_me v4.1.6, which was
|
||||
This file was extended by qbittorrent $as_me v4.1.8, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -7648,7 +7648,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
qbittorrent config.status v4.1.6
|
||||
qbittorrent config.status v4.1.8
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
AC_INIT([qbittorrent], [v4.1.6], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||
AC_INIT([qbittorrent], [v4.1.8], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_PROG_CC
|
||||
|
2
dist/mac/Info.plist
vendored
2
dist/mac/Info.plist
vendored
@@ -55,7 +55,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.1.6</string>
|
||||
<string>4.1.8</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>@EXECUTABLE@</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
2
dist/windows/options.nsi
vendored
2
dist/windows/options.nsi
vendored
@@ -27,7 +27,7 @@ XPStyle on
|
||||
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
|
||||
|
||||
; Program specific
|
||||
!define PROG_VERSION "4.1.6"
|
||||
!define PROG_VERSION "4.1.8"
|
||||
|
||||
!define MUI_FINISHPAGE_RUN
|
||||
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
||||
|
@@ -332,7 +332,11 @@ void Application::runExternalProgram(const BitTorrent::TorrentHandle *torrent) c
|
||||
|
||||
::LocalFree(args);
|
||||
#else
|
||||
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
|
||||
// Cannot give users shell environment by default, as doing so could
|
||||
// enable command injection via torrent name and other arguments
|
||||
// (especially when some automated download mechanism has been setup).
|
||||
// See: https://github.com/qbittorrent/qBittorrent/issues/10925
|
||||
QProcess::startDetached(program);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -101,6 +101,7 @@ QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
|
||||
#endif
|
||||
|
||||
server = new QLocalServer(this);
|
||||
server->setSocketOptions(QLocalServer::UserAccessOption);
|
||||
QString lockName = QDir(QDir::tempPath()).absolutePath()
|
||||
+ QLatin1Char('/') + socketName
|
||||
+ QLatin1String("-lockfile");
|
||||
@@ -191,6 +192,12 @@ void QtLocalPeer::receiveConnection()
|
||||
QByteArray uMsg;
|
||||
quint32 remaining;
|
||||
ds >> remaining;
|
||||
if (remaining > 65535) {
|
||||
// drop suspiciously large data
|
||||
delete socket;
|
||||
return;
|
||||
}
|
||||
|
||||
uMsg.resize(remaining);
|
||||
int got = 0;
|
||||
char* uMsgBuf = uMsg.data();
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -482,6 +482,7 @@ namespace BitTorrent
|
||||
void bottomTorrentsPriority(const QStringList &hashes);
|
||||
|
||||
// TorrentHandle interface
|
||||
void handleTorrentSaveResumeDataRequested(TorrentHandle *const torrent);
|
||||
void handleTorrentShareLimitChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentNameChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
|
||||
@@ -608,13 +609,13 @@ namespace BitTorrent
|
||||
|
||||
void updateSeedingLimitTimer();
|
||||
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
|
||||
void saveTorrentResumeData(TorrentHandle *const torrent);
|
||||
|
||||
void handleAlert(libtorrent::alert *a);
|
||||
void dispatchTorrentAlert(libtorrent::alert *a);
|
||||
void handleAddTorrentAlert(libtorrent::add_torrent_alert *p);
|
||||
void handleStateUpdateAlert(libtorrent::state_update_alert *p);
|
||||
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p);
|
||||
void handleMetadataReceivedAlert(const libtorrent::metadata_received_alert *p);
|
||||
void handleTorrentPausedAlert(const libtorrent::torrent_paused_alert *p);
|
||||
void handleFileErrorAlert(libtorrent::file_error_alert *p);
|
||||
void handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p);
|
||||
void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert *p);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -385,6 +385,12 @@ namespace BitTorrent
|
||||
private:
|
||||
typedef boost::function<void ()> EventTrigger;
|
||||
|
||||
#if (LIBTORRENT_VERSION_NUM < 10200)
|
||||
using LTFileIndex = int;
|
||||
#else
|
||||
using LTFileIndex = lt::file_index_t;
|
||||
#endif
|
||||
|
||||
void updateStatus();
|
||||
void updateStatus(const libtorrent::torrent_status &nativeStatus);
|
||||
void updateState();
|
||||
@@ -446,6 +452,10 @@ namespace BitTorrent
|
||||
QQueue<EventTrigger> m_moveFinishedTriggers;
|
||||
int m_renameCount;
|
||||
|
||||
// Until libtorrent provide an "old_name" field in `file_renamed_alert`
|
||||
// we will rely on this workaround to remove empty leftover folders
|
||||
QHash<LTFileIndex, QVector<QString>> m_oldPath;
|
||||
|
||||
bool m_useAutoTMM;
|
||||
|
||||
// Persistent data
|
||||
@@ -457,6 +467,7 @@ namespace BitTorrent
|
||||
qreal m_ratioLimit;
|
||||
int m_seedingTimeLimit;
|
||||
bool m_tempPathDisabled;
|
||||
bool m_fastresumeDataRejected;
|
||||
bool m_hasMissingFiles;
|
||||
bool m_hasRootFolder;
|
||||
bool m_needsToSetFirstLastPiecePriority;
|
||||
@@ -466,12 +477,15 @@ namespace BitTorrent
|
||||
|
||||
enum StartupState
|
||||
{
|
||||
NotStarted,
|
||||
Starting,
|
||||
Started
|
||||
Preparing, // torrent is preparing to start regular processing
|
||||
Starting, // torrent is prepared and starting to perform regular processing
|
||||
Started // torrent is performing regular processing
|
||||
};
|
||||
StartupState m_startupState = Preparing;
|
||||
// Handle torrent state when it starts performing some service job
|
||||
// being in Paused state so it might be unpaused internally and then paused again
|
||||
bool m_pauseWhenReady;
|
||||
|
||||
StartupState m_startupState = NotStarted;
|
||||
bool m_unchecked = false;
|
||||
};
|
||||
}
|
||||
|
@@ -340,8 +340,13 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(int fileIndex) const
|
||||
const libt::file_storage &files = nativeInfo()->files();
|
||||
const auto fileSize = files.file_size(fileIndex);
|
||||
const auto fileOffset = files.file_offset(fileIndex);
|
||||
return makeInterval(static_cast<int>(fileOffset / pieceLength()),
|
||||
static_cast<int>((fileOffset + fileSize - 1) / pieceLength()));
|
||||
|
||||
const int beginIdx = (fileOffset / pieceLength());
|
||||
const int endIdx = ((fileOffset + fileSize - 1) / pieceLength());
|
||||
|
||||
if (fileSize <= 0)
|
||||
return {beginIdx, 0};
|
||||
return makeInterval(beginIdx, endIdx);
|
||||
}
|
||||
|
||||
void TorrentInfo::renameFile(const int index, const QString &newPath)
|
||||
|
@@ -29,16 +29,11 @@
|
||||
|
||||
#include "tracker.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
|
||||
#include "base/global.h"
|
||||
#include "base/http/server.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/utils/bytearray.h"
|
||||
#include "base/utils/string.h"
|
||||
|
||||
// static limits
|
||||
static const int MAX_TORRENTS = 100;
|
||||
@@ -133,21 +128,7 @@ Http::Response Tracker::processRequest(const Http::Request &request, const Http:
|
||||
|
||||
void Tracker::respondToAnnounceRequest()
|
||||
{
|
||||
QMap<QString, QByteArray> queryParams;
|
||||
// Parse GET parameters
|
||||
using namespace Utils::ByteArray;
|
||||
for (const QByteArray ¶m : asConst(splitToViews(m_request.query, "&"))) {
|
||||
const int sepPos = param.indexOf('=');
|
||||
if (sepPos <= 0) continue; // ignores params without name
|
||||
|
||||
const QByteArray nameComponent = midView(param, 0, sepPos);
|
||||
const QByteArray valueComponent = midView(param, (sepPos + 1));
|
||||
|
||||
const QString paramName = QString::fromUtf8(QByteArray::fromPercentEncoding(nameComponent));
|
||||
const QByteArray paramValue = QByteArray::fromPercentEncoding(valueComponent);
|
||||
queryParams[paramName] = paramValue;
|
||||
}
|
||||
|
||||
const QMap<QString, QByteArray> &queryParams = m_request.query;
|
||||
TrackerAnnounceRequest announceReq;
|
||||
|
||||
// IP
|
||||
|
@@ -36,7 +36,6 @@
|
||||
|
||||
#include "base/http/irequesthandler.h"
|
||||
#include "base/http/responsebuilder.h"
|
||||
#include "base/http/types.h"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@@ -162,7 +162,7 @@ void FileSystemWatcher::processTorrentsInDir(const QDir &dir)
|
||||
const QStringList files = dir.entryList({"*.torrent", "*.magnet"}, QDir::Files);
|
||||
for (const QString &file : files) {
|
||||
const QString fileAbsPath = dir.absoluteFilePath(file);
|
||||
if (file.endsWith(".magnet"))
|
||||
if (file.endsWith(".magnet", Qt::CaseInsensitive))
|
||||
torrents << fileAbsPath;
|
||||
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid())
|
||||
torrents << fileAbsPath;
|
||||
|
@@ -36,6 +36,7 @@
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
|
||||
#include "base/global.h"
|
||||
#include "base/utils/bytearray.h"
|
||||
#include "base/utils/string.h"
|
||||
|
||||
@@ -180,14 +181,29 @@ bool RequestParser::parseRequestLine(const QString &line)
|
||||
m_request.method = match.captured(1);
|
||||
|
||||
// Request Target
|
||||
// URL components should be separated before percent-decoding
|
||||
// [rfc3986] 2.4 When to Encode or Decode
|
||||
const QByteArray url {match.captured(2).toLatin1()};
|
||||
const int sepPos = url.indexOf('?');
|
||||
const QByteArray pathComponent = ((sepPos == -1) ? url : Utils::ByteArray::midView(url, 0, sepPos));
|
||||
const QByteArray pathComponent = ((sepPos == -1) ? url : midView(url, 0, sepPos));
|
||||
|
||||
m_request.path = QString::fromUtf8(QByteArray::fromPercentEncoding(pathComponent));
|
||||
if (sepPos >= 0)
|
||||
m_request.query = url.mid(sepPos + 1);
|
||||
|
||||
if (sepPos >= 0) {
|
||||
const QByteArray query = midView(url, (sepPos + 1));
|
||||
|
||||
// [rfc3986] 2.4 When to Encode or Decode
|
||||
// URL components should be separated before percent-decoding
|
||||
for (const QByteArray ¶m : asConst(splitToViews(query, "&"))) {
|
||||
const int eqCharPos = param.indexOf('=');
|
||||
if (eqCharPos <= 0) continue; // ignores params without name
|
||||
|
||||
const QByteArray nameComponent = midView(param, 0, eqCharPos);
|
||||
const QByteArray valueComponent = midView(param, (eqCharPos + 1));
|
||||
const QString paramName = QString::fromUtf8(QByteArray::fromPercentEncoding(nameComponent).replace('+', ' '));
|
||||
const QByteArray paramValue = QByteArray::fromPercentEncoding(valueComponent).replace('+', ' ');
|
||||
|
||||
m_request.query[paramName] = paramValue;
|
||||
}
|
||||
}
|
||||
|
||||
// HTTP-version
|
||||
m_request.version = match.captured(3);
|
||||
|
@@ -97,8 +97,8 @@ namespace Http
|
||||
QString version;
|
||||
QString method;
|
||||
QString path;
|
||||
QByteArray query;
|
||||
QStringMap headers;
|
||||
QMap<QString, QByteArray> query;
|
||||
QStringMap posts;
|
||||
QVector<UploadedFile> files;
|
||||
};
|
||||
|
@@ -245,7 +245,7 @@ void Net::DownloadManager::applyProxySettings()
|
||||
|
||||
void Net::DownloadManager::handleReplyFinished(QNetworkReply *reply)
|
||||
{
|
||||
const ServiceID id = ServiceID::fromURL(reply->url());
|
||||
const ServiceID id = ServiceID::fromURL(reply->request().url());
|
||||
auto waitingJobsIter = m_waitingJobs.find(id);
|
||||
if ((waitingJobsIter == m_waitingJobs.end()) || waitingJobsIter.value().isEmpty()) {
|
||||
m_busyServices.remove(id);
|
||||
|
@@ -585,11 +585,13 @@ void Parser::parse_impl(const QByteArray &feedData)
|
||||
|
||||
emit finished(m_result);
|
||||
m_result.articles.clear(); // clear articles only
|
||||
m_articleIDs.clear();
|
||||
}
|
||||
|
||||
void Parser::parseRssArticle(QXmlStreamReader &xml)
|
||||
{
|
||||
QVariantHash article;
|
||||
QString altTorrentUrl;
|
||||
|
||||
while (!xml.atEnd()) {
|
||||
xml.readNext();
|
||||
@@ -605,6 +607,8 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
|
||||
else if (name == QLatin1String("enclosure")) {
|
||||
if (xml.attributes().value("type") == QLatin1String("application/x-bittorrent"))
|
||||
article[Article::KeyTorrentURL] = xml.attributes().value(QLatin1String("url")).toString();
|
||||
else if (xml.attributes().value("type").isEmpty())
|
||||
altTorrentUrl = xml.attributes().value(QLatin1String("url")).toString();
|
||||
}
|
||||
else if (name == QLatin1String("link")) {
|
||||
const QString text {xml.readElementText().trimmed()};
|
||||
@@ -631,7 +635,10 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
|
||||
}
|
||||
}
|
||||
|
||||
m_result.articles.prepend(article);
|
||||
if (article[Article::KeyTorrentURL].toString().isEmpty())
|
||||
article[Article::KeyTorrentURL] = altTorrentUrl;
|
||||
|
||||
addArticle(article);
|
||||
}
|
||||
|
||||
void Parser::parseRSSChannel(QXmlStreamReader &xml)
|
||||
@@ -726,7 +733,7 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
|
||||
}
|
||||
}
|
||||
|
||||
m_result.articles.prepend(article);
|
||||
addArticle(article);
|
||||
}
|
||||
|
||||
void Parser::parseAtomChannel(QXmlStreamReader &xml)
|
||||
@@ -756,3 +763,34 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::addArticle(QVariantHash article)
|
||||
{
|
||||
QVariant &torrentURL = article[Article::KeyTorrentURL];
|
||||
if (torrentURL.toString().isEmpty())
|
||||
torrentURL = article[Article::KeyLink];
|
||||
|
||||
// If item does not have an ID, fall back to some other identifier.
|
||||
QVariant &localId = article[Article::KeyId];
|
||||
if (localId.toString().isEmpty())
|
||||
localId = article.value(Article::KeyTorrentURL);
|
||||
if (localId.toString().isEmpty())
|
||||
localId = article.value(Article::KeyTitle);
|
||||
|
||||
if (localId.toString().isEmpty()) {
|
||||
// The article could not be uniquely identified
|
||||
// since it has no appropriate data.
|
||||
// Just ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_articleIDs.contains(localId.toString())) {
|
||||
// The article could not be uniquely identified
|
||||
// since the Feed has duplicate identifiers.
|
||||
// Just ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
m_articleIDs.insert(localId.toString());
|
||||
m_result.articles.prepend(article);
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QVariantHash>
|
||||
|
||||
@@ -65,9 +66,11 @@ namespace RSS
|
||||
void parseRSSChannel(QXmlStreamReader &xml);
|
||||
void parseAtomArticle(QXmlStreamReader &xml);
|
||||
void parseAtomChannel(QXmlStreamReader &xml);
|
||||
void addArticle(QVariantHash article);
|
||||
|
||||
QString m_baseUrl;
|
||||
ParsingResult m_result;
|
||||
QSet<QString> m_articleIDs;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -419,24 +419,10 @@ int Feed::updateArticles(const QList<QVariantHash> &loadedArticles)
|
||||
QVector<QVariantHash> newArticles;
|
||||
newArticles.reserve(loadedArticles.size());
|
||||
for (QVariantHash article : loadedArticles) {
|
||||
QVariant &torrentURL = article[Article::KeyTorrentURL];
|
||||
if (torrentURL.toString().isEmpty())
|
||||
torrentURL = article[Article::KeyLink];
|
||||
|
||||
// If item does not have an ID, fall back to some other identifier.
|
||||
QVariant &localId = article[Article::KeyId];
|
||||
if (localId.toString().isEmpty())
|
||||
localId = article.value(Article::KeyTorrentURL);
|
||||
if (localId.toString().isEmpty())
|
||||
localId = article.value(Article::KeyTitle);
|
||||
|
||||
if (localId.toString().isEmpty())
|
||||
continue;
|
||||
|
||||
// If article has no publication date we use feed update time as a fallback.
|
||||
// To prevent processing of "out-of-limit" articles we must not assign dates
|
||||
// that are earlier than the dates of existing articles.
|
||||
const Article *existingArticle = articleByGUID(localId.toString());
|
||||
const Article *existingArticle = articleByGUID(article[Article::KeyId].toString());
|
||||
if (existingArticle) {
|
||||
dummyPubDate = existingArticle->date().addMSecs(-1);
|
||||
continue;
|
||||
|
@@ -360,7 +360,7 @@ void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList)
|
||||
else if (!downloadInDefaultFolder(file))
|
||||
params.savePath = downloadPathTorrentFolder(file);
|
||||
|
||||
if (file.endsWith(".magnet")) {
|
||||
if (file.endsWith(".magnet", Qt::CaseInsensitive)) {
|
||||
QFile f(file);
|
||||
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QTextStream str(&f);
|
||||
|
@@ -38,8 +38,6 @@
|
||||
// See issue #3059 for more details (https://github.com/qbittorrent/qBittorrent/issues/3059).
|
||||
const char C_INFINITY[] = "∞";
|
||||
const char C_NON_BREAKING_SPACE[] = " ";
|
||||
const char C_UP[] = "▲";
|
||||
const char C_DOWN[] = "▼";
|
||||
const char C_COPYRIGHT[] = "©";
|
||||
const char C_THIN_SPACE[] = " ";
|
||||
const char C_UTP[] = "μTP";
|
||||
|
@@ -236,7 +236,19 @@ bool Utils::Fs::isValidFileSystemName(const QString &name, bool allowSeparators)
|
||||
{
|
||||
if (name.isEmpty()) return false;
|
||||
|
||||
const QRegularExpression regex(allowSeparators ? "[:?\"*<>|]" : "[\\\\/:?\"*<>|]");
|
||||
#if defined(Q_OS_WIN)
|
||||
const QRegularExpression regex {allowSeparators
|
||||
? QLatin1String("[:?\"*<>|]")
|
||||
: QLatin1String("[\\\\/:?\"*<>|]")};
|
||||
#elif defined(Q_OS_MACOS)
|
||||
const QRegularExpression regex {allowSeparators
|
||||
? QLatin1String("[\\0:]")
|
||||
: QLatin1String("[\\0/:]")};
|
||||
#else
|
||||
const QRegularExpression regex {allowSeparators
|
||||
? QLatin1String("[\\0]")
|
||||
: QLatin1String("[\\0/]")};
|
||||
#endif
|
||||
return !name.contains(regex);
|
||||
}
|
||||
|
||||
|
@@ -97,18 +97,29 @@ namespace
|
||||
}
|
||||
else if (leftChar.isDigit() && rightChar.isDigit()) {
|
||||
// Both are digits, compare the numbers
|
||||
const auto consumeNumber = [](const QString &str, int &pos) -> int
|
||||
|
||||
const auto numberView = [](const QString &str, int &pos) -> QStringRef
|
||||
{
|
||||
const int start = pos;
|
||||
while ((pos < str.size()) && str[pos].isDigit())
|
||||
++pos;
|
||||
return str.midRef(start, (pos - start)).toInt();
|
||||
return str.midRef(start, (pos - start));
|
||||
};
|
||||
|
||||
const int numL = consumeNumber(left, posL);
|
||||
const int numR = consumeNumber(right, posR);
|
||||
if (numL != numR)
|
||||
return (numL - numR);
|
||||
const QStringRef numViewL = numberView(left, posL);
|
||||
const QStringRef numViewR = numberView(right, posR);
|
||||
|
||||
if (numViewL.length() != numViewR.length())
|
||||
return (numViewL.length() - numViewR.length());
|
||||
|
||||
// both string/view has the same length
|
||||
for (int i = 0; i < numViewL.length(); ++i) {
|
||||
const QChar numL = numViewL.at(i);
|
||||
const QChar numR = numViewR.at(i);
|
||||
|
||||
if (numL != numR)
|
||||
return (numL.unicode() - numR.unicode());
|
||||
}
|
||||
|
||||
// String + digits do match and we haven't hit the end of both strings
|
||||
// then continue to consume the remainings
|
||||
@@ -202,3 +213,14 @@ TriStateBool Utils::String::parseTriStateBool(const QString &string)
|
||||
return TriStateBool::False;
|
||||
return TriStateBool::Undefined;
|
||||
}
|
||||
|
||||
QString Utils::String::join(const QVector<QStringRef> &strings, const QString &separator)
|
||||
{
|
||||
if (strings.empty())
|
||||
return {};
|
||||
|
||||
QString ret = strings[0].toString();
|
||||
for (int i = 1; i < strings.count(); ++i)
|
||||
ret += (separator + strings[i]);
|
||||
return ret;
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#define UTILS_STRING_H
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
class QByteArray;
|
||||
class QLatin1String;
|
||||
@@ -70,6 +71,8 @@ namespace Utils
|
||||
|
||||
bool parseBool(const QString &string, const bool defaultValue);
|
||||
TriStateBool parseTriStateBool(const QString &string);
|
||||
|
||||
QString join(const QVector<QStringRef> &strings, const QString &separator);
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -31,11 +31,11 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include <QScopedPointer>
|
||||
#include <QShortcut>
|
||||
|
||||
#include "base/bittorrent/addtorrentparams.h"
|
||||
#include "base/bittorrent/infohash.h"
|
||||
#include "base/bittorrent/torrentinfo.h"
|
||||
#include "base/settingvalue.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
@@ -72,11 +72,9 @@ public:
|
||||
static void show(QString source, QWidget *parent);
|
||||
|
||||
private slots:
|
||||
void showAdvancedSettings(bool show);
|
||||
void displayContentTreeMenu(const QPoint &);
|
||||
void updateDiskSpaceLabel();
|
||||
void onSavePathChanged(const QString &newPath);
|
||||
void renameSelectedFile();
|
||||
void updateMetadata(const BitTorrent::TorrentInfo &info);
|
||||
void handleDownloadFailed(const QString &url, const QString &reason);
|
||||
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
|
||||
@@ -99,7 +97,6 @@ private:
|
||||
void saveState();
|
||||
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = QString());
|
||||
void setupTreeview();
|
||||
void setCommentText(const QString &str) const;
|
||||
void setSavePath(const QString &newPath);
|
||||
|
||||
void showEvent(QShowEvent *event) override;
|
||||
@@ -115,6 +112,9 @@ private:
|
||||
int m_oldIndex;
|
||||
QScopedPointer<TorrentFileGuard> m_torrentGuard;
|
||||
BitTorrent::AddTorrentParams m_torrentParams;
|
||||
|
||||
CachedSettingValue<QSize> m_storeDialogSize;
|
||||
CachedSettingValue<QByteArray> m_storeSplitterState;
|
||||
};
|
||||
|
||||
#endif // ADDNEWTORRENTDIALOG_H
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -341,7 +341,7 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||
// When build as 32bit binary, set the maximum at less than 2GB to prevent crashes.
|
||||
// These macros may not be available on compilers other than MSVC and GCC
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
spinBoxCache.setMaximum(4096);
|
||||
spinBoxCache.setMaximum(33554431); // 32768GiB
|
||||
#else
|
||||
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
|
||||
spinBoxCache.setMaximum(1536);
|
||||
@@ -350,8 +350,8 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||
updateCacheSpinSuffix(spinBoxCache.value());
|
||||
addRow(DISK_CACHE, tr("Disk cache"), &spinBoxCache);
|
||||
// Disk cache expiry
|
||||
spinBoxCacheTTL.setMinimum(15);
|
||||
spinBoxCacheTTL.setMaximum(600);
|
||||
spinBoxCacheTTL.setMinimum(1);
|
||||
spinBoxCacheTTL.setMaximum(std::numeric_limits<int>::max());
|
||||
spinBoxCacheTTL.setValue(session->diskCacheTTL());
|
||||
spinBoxCacheTTL.setSuffix(tr(" s", " seconds"));
|
||||
addRow(DISK_CACHE_TTL, tr("Disk cache expiry interval"), &spinBoxCacheTTL);
|
||||
|
@@ -1158,8 +1158,8 @@ void MainWindow::closeEvent(QCloseEvent *e)
|
||||
#else
|
||||
const bool goToSystrayOnExit = pref->closeToTray();
|
||||
if (!m_forceExit && m_systrayIcon && goToSystrayOnExit && !this->isHidden()) {
|
||||
hide();
|
||||
e->accept();
|
||||
e->ignore();
|
||||
QTimer::singleShot(0, this, &QWidget::hide);
|
||||
if (!pref->closeToTrayNotified()) {
|
||||
showNotificationBaloon(tr("qBittorrent is closed to tray"), tr("This behavior can be changed in the settings. You won't be reminded again."));
|
||||
pref->setCloseToTrayNotified(true);
|
||||
|
@@ -304,7 +304,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||
.arg(tr("Supported parameters (case sensitive):")
|
||||
, tr("%N: Torrent name")
|
||||
, tr("%L: Category")
|
||||
, tr("%G: Tags (seperated by comma)")
|
||||
, tr("%G: Tags (separated by comma)")
|
||||
, tr("%F: Content path (same as root path for multifile torrent)")
|
||||
, tr("%R: Root path (first torrent subdirectory path)")
|
||||
, tr("%D: Save path")
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user