Compare commits

...

53 Commits

Author SHA1 Message Date
sledgehammer999
2c65b79640 Bump to 4.2.5 2020-04-25 00:29:44 +03:00
sledgehammer999
1ef504631d Update Changelog 2020-04-25 00:25:01 +03:00
sledgehammer999
5468f10184 Bump Web API version 2020-04-25 00:21:06 +03:00
sledgehammer999
5e249c4898 Sync translations from Transifex and run lupdate 2020-04-25 00:11:49 +03:00
Chocobo1
c67d863c93 Register datatype properly
Qt 5.9.5 doesn't seem to recognize it, this patch fixes it.
Fix up: d8401c76f5.
Related: #12601.
2020-04-25 00:01:49 +03:00
Sepro
6e375a38ef WebAPI: Change delimiter from string to char in preferences API 2020-04-25 00:01:47 +03:00
Sepro
0f6768f6ad WebAPI: Expand RSS related API
Added markAsRead API method with optional parameter for specifying single article.
Added the rss_smart_episode_filters and rss_download_repack_proper_episodes keys to preference api.
Added matchingArticles API method for retrieving articles that match specified rule.
2020-04-25 00:01:46 +03:00
Vladimir Golovnev (Glassez)
bdeb662cb0 Fix crash when torrent is deleted on limit reached 2020-04-25 00:01:44 +03:00
Chocobo1
b9b06d5384 Suppress misleading warning message
QString::split() was giving a non-empty QStringList, and
that caused Session::setBannedIPs() to emit misleading warning message.
2020-04-25 00:01:43 +03:00
Chocobo1
6a174b594b Add ability to send custom HTTP headers 2020-04-25 00:01:41 +03:00
schnurlos
f7cd5ac7c7 NSIS: Update german translation 2020-04-25 00:01:21 +03:00
sledgehammer999
50f676c305 Bump to 4.2.4 2020-04-22 18:49:52 +03:00
sledgehammer999
7103ae73cd Update Changelog 2020-04-22 18:46:15 +03:00
sledgehammer999
7d4b8b010f Sync translations from Transifex and run lupdate 2020-04-22 18:45:26 +03:00
an0n666
09bf033226 Use configured net interface even when it is missing 2020-04-22 18:19:33 +03:00
NotTsunami
4e62608802 WebUI: Fix UPnP lease duration get/set
Add missing setProperty and getProperty calls for the new UPnP lease
duration setting.

Fixes 6b4925d222.
Closes #12566.
2020-04-22 18:19:32 +03:00
Chocobo1
37b29bf91a Reduce ambiguity for selecting icons
Related: #12554.
2020-04-22 18:19:31 +03:00
thalieht
d67037136b Don't uncheck Authentication checkbox when changing proxy type
Closes #12525
2020-04-22 18:19:29 +03:00
thalieht
73292137b7 Make a few cosmetic changes in code 2020-04-22 18:19:28 +03:00
C.W. Betts
56f7a4e803 Update Info.plist
Wrap the UTTypeTagSpecifications in arrays, as Xcode does.
2020-04-22 18:19:26 +03:00
Chocobo1
1cea6a25af Add final specifier to GUI classes
Follow up d3d3f7dbb3.
2020-04-22 18:19:23 +03:00
Chocobo1
ebc704ef14 Reduce padding in class
TorrentHandleImpl size is reduced from 768 bytes to 736 bytes.
CreateTorrentParams size didn't change.
Size numbers are from x64.
2020-04-22 18:19:22 +03:00
Chocobo1
a2a1a78f44 Move initialization default values to header 2020-04-22 18:19:21 +03:00
Chocobo1
3a54d574b0 Reduce padding in structure
Log::Msg originally takes 32 bytes, now shrinks to 24 bytes.
Log::Peer originally takes 40 bytes, now shrinks to 32 bytes.
2020-04-22 18:19:20 +03:00
an0n666
d0be71c225 Change default upload slot choking limits 2020-04-22 18:19:19 +03:00
Chocobo1
7c04b4acd8 Add final specifier to classes
This allow compilers to generate more efficient code.
2020-04-22 18:19:18 +03:00
sledgehammer999
eda3747c08 Use static_cast for explicit type conversions 2020-04-22 18:19:17 +03:00
sledgehammer999
7d23ea1f80 TravisCI: Use libtorrent with deprecated functions disabled for Linux builds 2020-04-22 18:19:16 +03:00
Vladimir Golovnev (Glassez)
698ee94d0b Split TorrentHandle interface and implementation 2020-04-22 18:19:14 +03:00
jagannatharjun
65d1b588d9 Convert the Log widget to use custom View/Model
Co-authored-by: sledgehammer999 <hammered999@gmail.com>
2020-04-22 18:19:13 +03:00
an0n666
eea693979a Change default stop_tracker_timeout settings 2020-04-22 18:19:12 +03:00
Raif Atef
654bf85a71 Do not use 0.0.0.0 or [::] for outgoing interfaces
Fixes #12443
2020-04-22 18:19:11 +03:00
an0n666
8706a7c973 Remove deprecated strict super seeding mode from advanced settings 2020-04-22 18:19:10 +03:00
Chocobo1
439a2ef597 Fix date format for "Last seen complete"
Closes #12462.
2020-04-22 18:19:08 +03:00
Sepro
c5a7aa7668 Fix unable to add multiple peers in WebUI
Wrong delimiter was used.
2020-04-22 18:19:07 +03:00
Chocobo1
e5bf83a594 Preallocate output buffer 2020-04-22 18:19:06 +03:00
Chocobo1
2a3e64933b Fix header inclusion order 2020-04-22 18:19:05 +03:00
Chocobo1
35f8af32a3 Suppress unused variable warning on macOS 2020-04-22 18:19:04 +03:00
Chocobo1
37354a9e29 Avoid holding encoded resume data in memory
Now it the encoded resume data will be streamed to file instead of a
temporary buffer holding the whole of it.
2020-04-22 18:19:02 +03:00
József Sallai
7cb14e2a5b Detect python3 executable on Windows 2020-04-22 18:19:01 +03:00
Vladimir Golovnev (Glassez)
4aae7266a5 Save "resume data" when torrent storage is moved 2020-04-22 18:19:00 +03:00
Raif Atef
075245c915 Fix outgoing interface is not getting assigned
Assignment was missing in main branch of condition statement.
Closes https://github.com/qbittorrent/qBittorrent/issues/12421
2020-04-22 18:18:59 +03:00
adem
476707cc80 Remove white outline around mascot.png 2020-04-22 18:18:58 +03:00
Chocobo1
7b0b5e3d7f Avoid inefficient behavior
Since the class needs to be copy-constructible, there may be many
copies of an instance. So instead of writing to the device on every
destructor call, only flush buffer on the last destructor call.
2020-04-22 18:18:56 +03:00
Chocobo1
4142722303 Sort locale language list 2020-04-22 18:18:55 +03:00
NotTsunami
8ebb11f981 Set disk cache size for older libtorrent versions
Libtorrent versions older than 1.2.6 have a bug when setting disk
cache size to auto.

See 6c880159c9.
2020-04-22 18:18:54 +03:00
Chocobo1
80016db781 Fix wrong logic that disables "prevent sleeping" timer
Also update power management state early so we don't need to wait for
the timer timeout to have the effect.
2020-04-22 18:18:53 +03:00
Chocobo1
a9f43bd5d2 Clean up coding style 2020-04-22 18:18:52 +03:00
Chocobo1
2f0c3f047a Avoid holding entire file in memory
Previously we need a file buffer that is as large as the file size and
this could be a problem when user has less free memory available or
having very large data. Now with the help of `FileOutputIterator`,
we can have a much smaller, fixed size immediate file buffer and also
the code looks nice with `lt::bencode()`.
2020-04-22 18:18:50 +03:00
Vladimir Golovnev (Glassez)
f40a36ecb3 Fix sub-sorting of Transfer list
Closes #12330.
2020-04-22 18:18:50 +03:00
Chocobo1
a1ee1c0448 Remove redundant type attribute
It already defaults to `text/css` if value is absent (in HTML5).
2020-04-22 18:18:49 +03:00
Chocobo1
939f83bdd5 Fix mismatch ID 2020-04-22 18:18:48 +03:00
Chocobo1
e98a887286 Improve logging for errors
This commit also allows the strings to be translated.
2020-04-22 18:18:37 +03:00
233 changed files with 31786 additions and 29427 deletions

View File

@@ -55,14 +55,13 @@ addons:
apt: apt:
sources: sources:
# sources list: https://github.com/travis-ci/apt-source-safelist/blob/master/ubuntu.json # sources list: https://github.com/travis-ci/apt-source-safelist/blob/master/ubuntu.json
- sourceline: 'ppa:qbittorrent-team/qbittorrent-stable' - sourceline: 'ppa:qbittorrent-team/qbt-libtorrent-travisci'
- sourceline: 'ppa:beineri/opt-qt59-xenial' - sourceline: 'ppa:beineri/opt-qt59-xenial'
packages: packages:
# packages list: https://github.com/travis-ci/apt-package-safelist/blob/master/ubuntu-trusty # packages list: https://github.com/travis-ci/apt-package-safelist/blob/master/ubuntu-trusty
- [autoconf, automake, colormake] - [autoconf, automake, colormake]
- [libboost-dev, libboost-system-dev] - [libboost-dev, libboost-system-dev]
- libssl-dev - libssl-dev
- libtorrent-rasterbar-dev
- [qt59base, qt59svg, qt59tools] - [qt59base, qt59svg, qt59tools]
- zlib1g-dev - zlib1g-dev
# required for Qt 5.9 from 'beineri' PPA # required for Qt 5.9 from 'beineri' PPA
@@ -132,7 +131,7 @@ install:
ccache -V && ccache --show-stats && ccache --zero-stats ccache -V && ccache --show-stats && ccache --zero-stats
fi fi
- | - |
if [ "$libt_branch" = "RC_1_2" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then if [ "$libt_branch" = "RC_1_1" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then
wget https://builds.shiki.hu/travis/deb/version wget https://builds.shiki.hu/travis/deb/version
if ! cmp --quiet "version" "$HOME/travis/deb/version" ; then if ! cmp --quiet "version" "$HOME/travis/deb/version" ; then
echo "Cached files are different from server. Downloading new ones." echo "Cached files are different from server. Downloading new ones."
@@ -141,11 +140,16 @@ install:
mkdir "$HOME/travis/deb" mkdir "$HOME/travis/deb"
cp "version" $HOME/travis/deb cp "version" $HOME/travis/deb
cd "$HOME/travis/deb" cd "$HOME/travis/deb"
wget https://builds.shiki.hu/travis/deb/libtorrent-rasterbar-dev_1.2.x_amd64.deb wget https://builds.shiki.hu/travis/deb/libtorrent-rasterbar-dev_1.1.x_amd64.deb
wget https://builds.shiki.hu/travis/deb/libtorrent-rasterbar10_1.2.x_amd64.deb wget https://builds.shiki.hu/travis/deb/libtorrent-rasterbar9_1.1.x_amd64.deb
fi fi
sudo dpkg -i "$HOME/travis/deb/libtorrent-rasterbar-dev_1.2.x_amd64.deb" "$HOME/travis/deb/libtorrent-rasterbar10_1.2.x_amd64.deb" sudo dpkg -i "$HOME/travis/deb/libtorrent-rasterbar-dev_1.1.x_amd64.deb" "$HOME/travis/deb/libtorrent-rasterbar9_1.1.x_amd64.deb"
fi
- |
if [ "$libt_branch" = "RC_1_2" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then
# Will install latest 1.2.x daily build from the PPA
sudo apt-get -y install libtorrent-rasterbar-dev
fi fi
- | - |
if [ "$libt_branch" = "RC_1_1" ] && [ "$TRAVIS_OS_NAME" = "osx" ]; then if [ "$libt_branch" = "RC_1_1" ] && [ "$TRAVIS_OS_NAME" = "osx" ]; then

View File

@@ -1,3 +1,30 @@
Sat Apr 25 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.2.5
- BUGFIX: Fix crash when torrent is deleted on limit reached (glassez)
- BUGFIX: Register datatype properly (Chocobo1)
- WEBUI: Add ability to send custom HTTP headers (Chocobo1)
- WEBUI: Expand RSS related API (Sepro)
- WINDOWS: Installer: Update german translation (schnurlos)
Wed Apr 22 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.2.4
- BUGFIX: Fix sub-sorting of Transfer list (glassez)
- BUGFIX: Fix wrong logic that disables "prevent sleeping" timer (Chocobo1)
- BUGFIX: Set disk cache size for older libtorrent versions (NotTsunami)
- BUGFIX: Sort locale language list (Chocobo1)
- BUGFIX: Remove white outline around mascot.png (adem)
- BUGFIX: Various fixes in configuring the chosen network interface and not leaking the IP (Raif Atef, an0n666)
- BUGFIX: Save "resume data" when torrent storage is moved (glassez)
- BUGFIX: Avoid holding encoded resume data in memory (Chocobo1)
- BUGFIX: Fix date format for "Last seen complete" (Chocobo1)
- BUGFIX: Remove deprecated strict super seeding mode from advanced settings (an0n666)
- BUGFIX: Change default stop_tracker_timeout settings (an0n666)
- BUGFIX: Convert the Log widget to use custom View/Model (jagannatharjun)
- BUGFIX: Change default upload slot choking limits (an0n666)
- BUGFIX: Don't uncheck Authentication checkbox when changing proxy type (thalieht)
- BUGFIX: Reduce ambiguity for selecting tray icons (Chocobo1)
- WEBUI: Fix unable to add multiple peers in WebUI (Sepro)
- WEBUI: Fix UPnP lease duration get/set (NotTsunami)
- SEARCH: Detect python3 executable on Windows (József Sallai)
Wed Apr 01 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.2.3 Wed Apr 01 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.2.3
- FEATURE: Add logging for SOCKS5 proxy errors (Chocobo1) - FEATURE: Add logging for SOCKS5 proxy errors (Chocobo1)
- FEATURE: Add UPnP lease duration advanced option (NotTsunami) - FEATURE: Add UPnP lease duration advanced option (NotTsunami)

24
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for qbittorrent v4.2.3. # Generated by GNU Autoconf 2.69 for qbittorrent v4.2.5.
# #
# Report bugs to <bugs.qbittorrent.org>. # Report bugs to <bugs.qbittorrent.org>.
# #
@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='qbittorrent' PACKAGE_NAME='qbittorrent'
PACKAGE_TARNAME='qbittorrent' PACKAGE_TARNAME='qbittorrent'
PACKAGE_VERSION='v4.2.3' PACKAGE_VERSION='v4.2.5'
PACKAGE_STRING='qbittorrent v4.2.3' PACKAGE_STRING='qbittorrent v4.2.5'
PACKAGE_BUGREPORT='bugs.qbittorrent.org' PACKAGE_BUGREPORT='bugs.qbittorrent.org'
PACKAGE_URL='https://www.qbittorrent.org/' PACKAGE_URL='https://www.qbittorrent.org/'
@@ -1302,7 +1302,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # 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. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures qbittorrent v4.2.3 to adapt to many kinds of systems. \`configure' configures qbittorrent v4.2.5 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1373,7 +1373,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of qbittorrent v4.2.3:";; short | recursive ) echo "Configuration of qbittorrent v4.2.5:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@@ -1509,7 +1509,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
qbittorrent configure v4.2.3 qbittorrent configure v4.2.5
generated by GNU Autoconf 2.69 generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc. Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1648,7 +1648,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by qbittorrent $as_me v4.2.3, which was It was created by qbittorrent $as_me v4.2.5, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@ $ $0 $@
@@ -3826,7 +3826,7 @@ fi
# Define the identity of the package. # Define the identity of the package.
PACKAGE='qbittorrent' PACKAGE='qbittorrent'
VERSION='v4.2.3' VERSION='v4.2.5'
cat >>confdefs.h <<_ACEOF cat >>confdefs.h <<_ACEOF
@@ -6343,7 +6343,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by qbittorrent $as_me v4.2.3, which was This file was extended by qbittorrent $as_me v4.2.5, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@@ -6401,7 +6401,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\ ac_cs_version="\\
qbittorrent config.status v4.2.3 qbittorrent config.status v4.2.5
configured by $0, generated by GNU Autoconf 2.69, configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"
@@ -7659,7 +7659,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by qbittorrent $as_me v4.2.3, which was This file was extended by qbittorrent $as_me v4.2.5, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@@ -7717,7 +7717,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\ ac_cs_version="\\
qbittorrent config.status v4.2.3 qbittorrent config.status v4.2.5
configured by $0, generated by GNU Autoconf 2.69, configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@@ -1,4 +1,4 @@
AC_INIT([qbittorrent], [v4.2.3], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/]) AC_INIT([qbittorrent], [v4.2.5], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC AC_PROG_CC

10
dist/mac/Info.plist vendored
View File

@@ -55,7 +55,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>4.2.3</string> <string>4.2.5</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>@EXECUTABLE@</string> <string>@EXECUTABLE@</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@@ -88,13 +88,17 @@
<key>UTTypeTagSpecification</key> <key>UTTypeTagSpecification</key>
<dict> <dict>
<key>com.apple.ostype</key> <key>com.apple.ostype</key>
<string>TORR</string> <array>
<string>TORR</string>
</array>
<key>public.filename-extension</key> <key>public.filename-extension</key>
<array> <array>
<string>torrent</string> <string>torrent</string>
</array> </array>
<key>public.mime-type</key> <key>public.mime-type</key>
<string>application/x-bittorrent</string> <array>
<string>application/x-bittorrent</string>
</array>
</dict> </dict>
</dict> </dict>
</array> </array>

View File

@@ -74,6 +74,6 @@
<url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url> <url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url>
<content_rating type="oars-1.1"/> <content_rating type="oars-1.1"/>
<releases> <releases>
<release version="4.2.3" date="2020-04-01"/> <release version="4.2.5" date="2020-04-25"/>
</releases> </releases>
</component> </component>

View File

@@ -15,7 +15,7 @@ LangString inst_magnet ${LANG_GERMAN} "Öffne Magnet-Links mit qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule" ;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_GERMAN} "Regel in der Windows Firewall hinzufügen" LangString inst_firewall ${LANG_GERMAN} "Regel in der Windows Firewall hinzufügen"
;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)" ;LangString inst_pathlimit ${LANG_ENGLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_GERMAN} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)" LangString inst_pathlimit ${LANG_GERMAN} "Deaktiviere die Windows-Pfadlängenbeschränkung (260 Zeichen Beschränkung von MAX_PATH - erfordert Windows 10 1607 oder höher)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule" ;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_GERMAN} "Füge Regel in der Windows Firewall hinzu" LangString inst_firewallinfo ${LANG_GERMAN} "Füge Regel in der Windows Firewall hinzu"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing." ;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."

View File

@@ -28,7 +28,7 @@ XPStyle on
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path !define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
; Program specific ; Program specific
!define PROG_VERSION "4.2.3" !define PROG_VERSION "4.2.5"
!define MUI_FINISHPAGE_RUN !define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun !define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun

View File

@@ -61,6 +61,7 @@
#endif // Q_OS_MACOS #endif // Q_OS_MACOS
#endif #endif
#include "base/bittorrent/infohash.h"
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h" #include "base/bittorrent/torrenthandle.h"
#include "base/exceptions.h" #include "base/exceptions.h"
@@ -133,6 +134,7 @@ Application::Application(int &argc, char **argv)
, m_commandLineArgs(parseCommandLine(this->arguments())) , m_commandLineArgs(parseCommandLine(this->arguments()))
{ {
qRegisterMetaType<Log::Msg>("Log::Msg"); qRegisterMetaType<Log::Msg>("Log::Msg");
qRegisterMetaType<Log::Peer>("Log::Peer");
setApplicationName("qBittorrent"); setApplicationName("qBittorrent");
setOrganizationDomain("qbittorrent.org"); setOrganizationDomain("qbittorrent.org");

View File

@@ -68,7 +68,7 @@ namespace RSS
class AutoDownloader; class AutoDownloader;
} }
class Application : public BaseApplication class Application final : public BaseApplication
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(Application) Q_DISABLE_COPY(Application)

View File

@@ -77,7 +77,7 @@
namespace QtLP_Private namespace QtLP_Private
{ {
class QtLockedFile : public QFile class QtLockedFile final : public QFile
{ {
public: public:
enum LockMode enum LockMode

View File

@@ -22,6 +22,7 @@ bittorrent/session.h
bittorrent/sessionstatus.h bittorrent/sessionstatus.h
bittorrent/torrentcreatorthread.h bittorrent/torrentcreatorthread.h
bittorrent/torrenthandle.h bittorrent/torrenthandle.h
bittorrent/torrenthandleimpl.h
bittorrent/torrentinfo.h bittorrent/torrentinfo.h
bittorrent/tracker.h bittorrent/tracker.h
bittorrent/trackerentry.h bittorrent/trackerentry.h
@@ -58,6 +59,7 @@ utils/bytearray.h
utils/foreignapps.h utils/foreignapps.h
utils/fs.h utils/fs.h
utils/gzip.h utils/gzip.h
utils/io.h
utils/misc.h utils/misc.h
utils/net.h utils/net.h
utils/password.h utils/password.h
@@ -99,6 +101,7 @@ bittorrent/private/statistics.cpp
bittorrent/session.cpp bittorrent/session.cpp
bittorrent/torrentcreatorthread.cpp bittorrent/torrentcreatorthread.cpp
bittorrent/torrenthandle.cpp bittorrent/torrenthandle.cpp
bittorrent/torrenthandleimpl.cpp
bittorrent/torrentinfo.cpp bittorrent/torrentinfo.cpp
bittorrent/tracker.cpp bittorrent/tracker.cpp
bittorrent/trackerentry.cpp bittorrent/trackerentry.cpp
@@ -133,6 +136,7 @@ utils/bytearray.cpp
utils/foreignapps.cpp utils/foreignapps.cpp
utils/fs.cpp utils/fs.cpp
utils/gzip.cpp utils/gzip.cpp
utils/io.cpp
utils/misc.cpp utils/misc.cpp
utils/net.cpp utils/net.cpp
utils/password.cpp utils/password.cpp

View File

@@ -21,6 +21,7 @@ HEADERS += \
$$PWD/bittorrent/sessionstatus.h \ $$PWD/bittorrent/sessionstatus.h \
$$PWD/bittorrent/torrentcreatorthread.h \ $$PWD/bittorrent/torrentcreatorthread.h \
$$PWD/bittorrent/torrenthandle.h \ $$PWD/bittorrent/torrenthandle.h \
$$PWD/bittorrent/torrenthandleimpl.h \
$$PWD/bittorrent/torrentinfo.h \ $$PWD/bittorrent/torrentinfo.h \
$$PWD/bittorrent/tracker.h \ $$PWD/bittorrent/tracker.h \
$$PWD/bittorrent/trackerentry.h \ $$PWD/bittorrent/trackerentry.h \
@@ -73,6 +74,7 @@ HEADERS += \
$$PWD/utils/foreignapps.h \ $$PWD/utils/foreignapps.h \
$$PWD/utils/fs.h \ $$PWD/utils/fs.h \
$$PWD/utils/gzip.h \ $$PWD/utils/gzip.h \
$$PWD/utils/io.h \
$$PWD/utils/misc.h \ $$PWD/utils/misc.h \
$$PWD/utils/net.h \ $$PWD/utils/net.h \
$$PWD/utils/password.h \ $$PWD/utils/password.h \
@@ -98,6 +100,7 @@ SOURCES += \
$$PWD/bittorrent/session.cpp \ $$PWD/bittorrent/session.cpp \
$$PWD/bittorrent/torrentcreatorthread.cpp \ $$PWD/bittorrent/torrentcreatorthread.cpp \
$$PWD/bittorrent/torrenthandle.cpp \ $$PWD/bittorrent/torrenthandle.cpp \
$$PWD/bittorrent/torrenthandleimpl.cpp \
$$PWD/bittorrent/torrentinfo.cpp \ $$PWD/bittorrent/torrentinfo.cpp \
$$PWD/bittorrent/tracker.cpp \ $$PWD/bittorrent/tracker.cpp \
$$PWD/bittorrent/trackerentry.cpp \ $$PWD/bittorrent/trackerentry.cpp \
@@ -143,6 +146,7 @@ SOURCES += \
$$PWD/utils/foreignapps.cpp \ $$PWD/utils/foreignapps.cpp \
$$PWD/utils/fs.cpp \ $$PWD/utils/fs.cpp \
$$PWD/utils/gzip.cpp \ $$PWD/utils/gzip.cpp \
$$PWD/utils/io.cpp \
$$PWD/utils/misc.cpp \ $$PWD/utils/misc.cpp \
$$PWD/utils/net.cpp \ $$PWD/utils/net.cpp \
$$PWD/utils/password.cpp \ $$PWD/utils/password.cpp \

View File

@@ -28,6 +28,8 @@
#include "peerinfo.h" #include "peerinfo.h"
#include <libtorrent/version.hpp>
#include <QBitArray> #include <QBitArray>
#include "base/bittorrent/torrenthandle.h" #include "base/bittorrent/torrenthandle.h"

View File

@@ -35,7 +35,7 @@
class QDataStream; class QDataStream;
class FilterParserThread : public QThread class FilterParserThread final : public QThread
{ {
Q_OBJECT Q_OBJECT

View File

@@ -31,7 +31,7 @@
#include <libtorrent/extensions.hpp> #include <libtorrent/extensions.hpp>
#include <libtorrent/version.hpp> #include <libtorrent/version.hpp>
class NativeSessionExtension : public lt::plugin class NativeSessionExtension final : public lt::plugin
{ {
#if (LIBTORRENT_VERSION_NUM >= 10200) #if (LIBTORRENT_VERSION_NUM >= 10200)
lt::feature_flags_t implemented_features() override; lt::feature_flags_t implemented_features() override;

View File

@@ -47,7 +47,7 @@ namespace
#if (LIBTORRENT_VERSION_NUM < 10200) #if (LIBTORRENT_VERSION_NUM < 10200)
return torrentStatus.auto_managed; return torrentStatus.auto_managed;
#else #else
return bool {torrentStatus.flags & lt::torrent_flags::auto_managed}; return static_cast<bool>(torrentStatus.flags & lt::torrent_flags::auto_managed);
#endif #endif
} }
} }

View File

@@ -32,7 +32,7 @@
#include <libtorrent/torrent_handle.hpp> #include <libtorrent/torrent_handle.hpp>
#include <libtorrent/version.hpp> #include <libtorrent/version.hpp>
class NativeTorrentExtension : public lt::torrent_plugin class NativeTorrentExtension final : public lt::torrent_plugin
{ {
public: public:
explicit NativeTorrentExtension(const lt::torrent_handle &torrentHandle); explicit NativeTorrentExtension(const lt::torrent_handle &torrentHandle);

View File

@@ -44,7 +44,7 @@ using LTPortMapping = int;
using LTPortMapping = lt::port_mapping_t; using LTPortMapping = lt::port_mapping_t;
#endif #endif
class PortForwarderImpl : public Net::PortForwarder class PortForwarderImpl final : public Net::PortForwarder
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(PortForwarderImpl) Q_DISABLE_COPY(PortForwarderImpl)

View File

@@ -28,11 +28,15 @@
#include "resumedatasavingmanager.h" #include "resumedatasavingmanager.h"
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#include <QByteArray> #include <QByteArray>
#include <QSaveFile> #include <QSaveFile>
#include "base/logger.h" #include "base/logger.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/io.h"
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath) ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
: m_resumeDataDir(resumeFolderPath) : m_resumeDataDir(resumeFolderPath)
@@ -44,12 +48,27 @@ void ResumeDataSavingManager::save(const QString &filename, const QByteArray &da
const QString filepath = m_resumeDataDir.absoluteFilePath(filename); const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
QSaveFile file {filepath}; QSaveFile file {filepath};
if (file.open(QIODevice::WriteOnly)) { if (!file.open(QIODevice::WriteOnly) || (file.write(data) != data.size()) || !file.commit()) {
file.write(data); LogMsg(tr("Couldn't save data to '%1'. Error: %2")
if (!file.commit()) { .arg(filepath, file.errorString()), Log::CRITICAL);
Logger::instance()->addMessage(QString("Couldn't save data in '%1'. Error: %2") }
.arg(filepath, file.errorString()), Log::WARNING); }
}
void ResumeDataSavingManager::save(const QString &filename, const std::shared_ptr<lt::entry> &data) const
{
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
QSaveFile file {filepath};
if (!file.open(QIODevice::WriteOnly)) {
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::CRITICAL);
return;
}
lt::bencode(Utils::IO::FileDeviceOutputIterator {file}, *data);
if ((file.error() != QFileDevice::NoError) || !file.commit()) {
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::CRITICAL);
} }
} }

View File

@@ -28,6 +28,10 @@
#pragma once #pragma once
#include <memory>
#include <libtorrent/fwd.hpp>
#include <QDir> #include <QDir>
#include <QObject> #include <QObject>
@@ -43,8 +47,9 @@ public:
public slots: public slots:
void save(const QString &filename, const QByteArray &data) const; void save(const QString &filename, const QByteArray &data) const;
void save(const QString &filename, const std::shared_ptr<lt::entry> &data) const;
void remove(const QString &filename) const; void remove(const QString &filename) const;
private: private:
QDir m_resumeDataDir; const QDir m_resumeDataDir;
}; };

View File

@@ -40,25 +40,6 @@
#include <iphlpapi.h> #include <iphlpapi.h>
#endif #endif
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QHostAddress>
#include <QNetworkAddressEntry>
#include <QNetworkConfigurationManager>
#include <QNetworkInterface>
#include <QRegularExpression>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QUuid>
#ifdef Q_OS_WIN
// TODO: Remove together with fixBrokenSavePath()
#define NEED_TO_FIX_BROKEN_PATH
#include <QSaveFile>
#endif
#include <libtorrent/alert_types.hpp> #include <libtorrent/alert_types.hpp>
#include <libtorrent/bdecode.hpp> #include <libtorrent/bdecode.hpp>
#include <libtorrent/bencode.hpp> #include <libtorrent/bencode.hpp>
@@ -78,6 +59,25 @@
#include <libtorrent/read_resume_data.hpp> #include <libtorrent/read_resume_data.hpp>
#endif #endif
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QHostAddress>
#include <QNetworkAddressEntry>
#include <QNetworkConfigurationManager>
#include <QNetworkInterface>
#include <QRegularExpression>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QUuid>
#ifdef Q_OS_WIN
// TODO: Remove together with fixBrokenSavePath()
#define NEED_TO_FIX_BROKEN_PATH
#include <QSaveFile>
#endif
#include "base/algorithm.h" #include "base/algorithm.h"
#include "base/exceptions.h" #include "base/exceptions.h"
#include "base/global.h" #include "base/global.h"
@@ -101,7 +101,7 @@
#include "private/portforwarderimpl.h" #include "private/portforwarderimpl.h"
#include "private/resumedatasavingmanager.h" #include "private/resumedatasavingmanager.h"
#include "private/statistics.h" #include "private/statistics.h"
#include "torrenthandle.h" #include "torrenthandleimpl.h"
#include "tracker.h" #include "tracker.h"
#include "trackerentry.h" #include "trackerentry.h"
@@ -438,7 +438,11 @@ Session::Session(QObject *parent)
, m_asyncIOThreads(BITTORRENT_SESSION_KEY("AsyncIOThreadsCount"), 4) , m_asyncIOThreads(BITTORRENT_SESSION_KEY("AsyncIOThreadsCount"), 4)
, m_filePoolSize(BITTORRENT_SESSION_KEY("FilePoolSize"), 40) , m_filePoolSize(BITTORRENT_SESSION_KEY("FilePoolSize"), 40)
, m_checkingMemUsage(BITTORRENT_SESSION_KEY("CheckingMemUsageSize"), 32) , m_checkingMemUsage(BITTORRENT_SESSION_KEY("CheckingMemUsageSize"), 32)
#if (LIBTORRENT_VERSION_NUM >= 10206)
, m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), -1) , m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), -1)
#else
, m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), 64)
#endif
, m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60) , m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60)
, m_useOSCache(BITTORRENT_SESSION_KEY("UseOSCache"), true) , m_useOSCache(BITTORRENT_SESSION_KEY("UseOSCache"), true)
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
@@ -467,12 +471,15 @@ Session::Session(QObject *parent)
, m_ignoreLimitsOnLAN(BITTORRENT_SESSION_KEY("IgnoreLimitsOnLAN"), false) , m_ignoreLimitsOnLAN(BITTORRENT_SESSION_KEY("IgnoreLimitsOnLAN"), false)
, m_includeOverheadInLimits(BITTORRENT_SESSION_KEY("IncludeOverheadInLimits"), false) , m_includeOverheadInLimits(BITTORRENT_SESSION_KEY("IncludeOverheadInLimits"), false)
, m_announceIP(BITTORRENT_SESSION_KEY("AnnounceIP")) , m_announceIP(BITTORRENT_SESSION_KEY("AnnounceIP"))
#if (LIBTORRENT_VERSION_NUM >= 10206)
, m_stopTrackerTimeout(BITTORRENT_SESSION_KEY("StopTrackerTimeout"), 5)
#else
, m_stopTrackerTimeout(BITTORRENT_SESSION_KEY("StopTrackerTimeout"), 1) , m_stopTrackerTimeout(BITTORRENT_SESSION_KEY("StopTrackerTimeout"), 1)
, m_isSuperSeedingEnabled(BITTORRENT_SESSION_KEY("SuperSeedingEnabled"), false) #endif
, m_maxConnections(BITTORRENT_SESSION_KEY("MaxConnections"), 500, lowerLimited(0, -1)) , m_maxConnections(BITTORRENT_SESSION_KEY("MaxConnections"), 500, lowerLimited(0, -1))
, m_maxUploads(BITTORRENT_SESSION_KEY("MaxUploads"), -1, lowerLimited(0, -1)) , m_maxUploads(BITTORRENT_SESSION_KEY("MaxUploads"), 20, lowerLimited(0, -1))
, m_maxConnectionsPerTorrent(BITTORRENT_SESSION_KEY("MaxConnectionsPerTorrent"), 100, lowerLimited(0, -1)) , m_maxConnectionsPerTorrent(BITTORRENT_SESSION_KEY("MaxConnectionsPerTorrent"), 100, lowerLimited(0, -1))
, m_maxUploadsPerTorrent(BITTORRENT_SESSION_KEY("MaxUploadsPerTorrent"), -1, lowerLimited(0, -1)) , m_maxUploadsPerTorrent(BITTORRENT_SESSION_KEY("MaxUploadsPerTorrent"), 4, lowerLimited(0, -1))
, m_btProtocol(BITTORRENT_SESSION_KEY("BTProtocol"), BTProtocol::Both , m_btProtocol(BITTORRENT_SESSION_KEY("BTProtocol"), BTProtocol::Both
, clampValue(BTProtocol::Both, BTProtocol::UTP)) , clampValue(BTProtocol::Both, BTProtocol::UTP))
, m_isUTPRateLimited(BITTORRENT_SESSION_KEY("uTPRateLimited"), true) , m_isUTPRateLimited(BITTORRENT_SESSION_KEY("uTPRateLimited"), true)
@@ -657,7 +664,7 @@ void Session::setTempPathEnabled(const bool enabled)
{ {
if (enabled != isTempPathEnabled()) { if (enabled != isTempPathEnabled()) {
m_isTempPathEnabled = enabled; m_isTempPathEnabled = enabled;
for (TorrentHandle *const torrent : asConst(m_torrents)) for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->handleTempPathChanged(); torrent->handleTempPathChanged();
} }
} }
@@ -671,7 +678,7 @@ void Session::setAppendExtensionEnabled(const bool enabled)
{ {
if (isAppendExtensionEnabled() != enabled) { if (isAppendExtensionEnabled() != enabled) {
// append or remove .!qB extension for incomplete files // append or remove .!qB extension for incomplete files
for (TorrentHandle *const torrent : asConst(m_torrents)) for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->handleAppendExtensionToggled(); torrent->handleAppendExtensionToggled();
m_isAppendExtensionEnabled = enabled; m_isAppendExtensionEnabled = enabled;
@@ -822,12 +829,12 @@ bool Session::editCategory(const QString &name, const QString &savePath)
m_categories[name] = savePath; m_categories[name] = savePath;
m_storedCategories = map_cast(m_categories); m_storedCategories = map_cast(m_categories);
if (isDisableAutoTMMWhenCategorySavePathChanged()) { if (isDisableAutoTMMWhenCategorySavePathChanged()) {
for (TorrentHandle *const torrent : asConst(torrents())) for (TorrentHandleImpl *const torrent : asConst(m_torrents))
if (torrent->category() == name) if (torrent->category() == name)
torrent->setAutoTMMEnabled(false); torrent->setAutoTMMEnabled(false);
} }
else { else {
for (TorrentHandle *const torrent : asConst(torrents())) for (TorrentHandleImpl *const torrent : asConst(m_torrents))
if (torrent->category() == name) if (torrent->category() == name)
torrent->handleCategorySavePathChanged(); torrent->handleCategorySavePathChanged();
} }
@@ -837,7 +844,7 @@ bool Session::editCategory(const QString &name, const QString &savePath)
bool Session::removeCategory(const QString &name) bool Session::removeCategory(const QString &name)
{ {
for (TorrentHandle *const torrent : asConst(torrents())) for (TorrentHandleImpl *const torrent : asConst(m_torrents))
if (torrent->belongsToCategory(name)) if (torrent->belongsToCategory(name))
torrent->setCategory(""); torrent->setCategory("");
@@ -924,7 +931,7 @@ bool Session::addTag(const QString &tag)
bool Session::removeTag(const QString &tag) bool Session::removeTag(const QString &tag)
{ {
if (m_tags.remove(tag)) { if (m_tags.remove(tag)) {
for (TorrentHandle *const torrent : asConst(torrents())) for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->removeTag(tag); torrent->removeTag(tag);
m_storedTags = m_tags.values(); m_storedTags = m_tags.values();
emit tagRemoved(tag); emit tagRemoved(tag);
@@ -1439,8 +1446,6 @@ void Session::loadLTSettings(lt::settings_pack &settingsPack)
settingsPack.set_str(lt::settings_pack::announce_ip, announceIP().toStdString()); settingsPack.set_str(lt::settings_pack::announce_ip, announceIP().toStdString());
// Stop tracker timeout // Stop tracker timeout
settingsPack.set_int(lt::settings_pack::stop_tracker_timeout, stopTrackerTimeout()); settingsPack.set_int(lt::settings_pack::stop_tracker_timeout, stopTrackerTimeout());
// Super seeding
settingsPack.set_bool(lt::settings_pack::strict_super_seeding, isSuperSeedingEnabled());
// * Max connections limit // * Max connections limit
settingsPack.set_int(lt::settings_pack::connections_limit, maxConnections()); settingsPack.set_int(lt::settings_pack::connections_limit, maxConnections());
// * Global max upload slots // * Global max upload slots
@@ -1529,10 +1534,13 @@ void Session::configureNetworkInterfaces(lt::settings_pack &settingsPack)
for (const QString &ip : asConst(getListeningIPs())) { for (const QString &ip : asConst(getListeningIPs())) {
const QHostAddress addr {ip}; const QHostAddress addr {ip};
if (!addr.isNull()) { if (!addr.isNull()) {
endpoints << ((addr.protocol() == QAbstractSocket::IPv6Protocol) const QString ip = ((addr.protocol() == QAbstractSocket::IPv6Protocol)
? ('[' + Utils::Net::canonicalIPv6Addr(addr).toString() + ']') ? ('[' + Utils::Net::canonicalIPv6Addr(addr).toString() + ']')
: addr.toString()) : addr.toString());
+ portString; endpoints << (ip + portString);
if ((ip != "0.0.0.0") && (ip != "[::]"))
outgoingInterfaces << ip;
} }
else { else {
// ip holds an interface name // ip holds an interface name
@@ -1547,6 +1555,10 @@ void Session::configureNetworkInterfaces(lt::settings_pack &settingsPack)
} }
else { else {
LogMsg(tr("Could not get GUID of network interface: %1").arg(ip) , Log::WARNING); LogMsg(tr("Could not get GUID of network interface: %1").arg(ip) , Log::WARNING);
// Since we can't get the GUID, we'll pass the interface name instead.
// Otherwise an empty string will be passed to outgoing_interface which will cause IP leak.
endpoints << (ip + portString);
outgoingInterfaces << ip;
} }
#else #else
endpoints << (ip + portString); endpoints << (ip + portString);
@@ -1555,24 +1567,6 @@ void Session::configureNetworkInterfaces(lt::settings_pack &settingsPack)
} }
} }
if (outgoingInterfaces.isEmpty()) {
#ifdef Q_OS_WIN
// On Vista+ versions and after Qt 5.5 QNetworkInterface::name() returns
// the interface's LUID and not the GUID.
// Libtorrent expects GUIDs for the 'outgoing_interfaces' setting.
const QString netInterface = networkInterface();
if (!netInterface.isEmpty()) {
const QString guid = convertIfaceNameToGuid(netInterface);
if (!guid.isEmpty())
outgoingInterfaces << guid;
else
LogMsg(tr("Could not get GUID of network interface: %1").arg(netInterface) , Log::WARNING);
}
#else
outgoingInterfaces << networkInterface();
#endif // Q_OS_WIN
}
const QString finalEndpoints = endpoints.join(','); const QString finalEndpoints = endpoints.join(',');
settingsPack.set_str(lt::settings_pack::listen_interfaces, finalEndpoints.toStdString()); settingsPack.set_str(lt::settings_pack::listen_interfaces, finalEndpoints.toStdString());
LogMsg(tr("Trying to listen on: %1", "e.g: Trying to listen on: 192.168.0.1:6881") LogMsg(tr("Trying to listen on: %1", "e.g: Trying to listen on: 192.168.0.1:6881")
@@ -1699,7 +1693,10 @@ void Session::processShareLimits()
{ {
qDebug("Processing share limits..."); qDebug("Processing share limits...");
for (TorrentHandle *const torrent : asConst(torrents())) { // We shouldn't iterate over `m_torrents` in the loop below
// since `deleteTorrent()` modifies it indirectly
const QHash<InfoHash, TorrentHandleImpl *> torrents {m_torrents};
for (TorrentHandleImpl *const torrent : torrents) {
if (torrent->isSeed() && !torrent->isForced()) { if (torrent->isSeed() && !torrent->isForced()) {
if (torrent->ratioLimit() != TorrentHandle::NO_RATIO_LIMIT) { if (torrent->ratioLimit() != TorrentHandle::NO_RATIO_LIMIT) {
const qreal ratio = torrent->realRatio(); const qreal ratio = torrent->realRatio();
@@ -1791,7 +1788,7 @@ TorrentHandle *Session::findTorrent(const InfoHash &hash) const
bool Session::hasActiveTorrents() const bool Session::hasActiveTorrents() const
{ {
return std::any_of(m_torrents.begin(), m_torrents.end(), [](TorrentHandle *torrent) return std::any_of(m_torrents.begin(), m_torrents.end(), [](TorrentHandleImpl *torrent)
{ {
return TorrentFilter::ActiveTorrent.match(torrent); return TorrentFilter::ActiveTorrent.match(torrent);
}); });
@@ -1799,7 +1796,7 @@ bool Session::hasActiveTorrents() const
bool Session::hasUnfinishedTorrents() const bool Session::hasUnfinishedTorrents() const
{ {
return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentHandle *torrent) return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentHandleImpl *torrent)
{ {
return (!torrent->isSeed() && !torrent->isPaused()); return (!torrent->isSeed() && !torrent->isPaused());
}); });
@@ -1807,7 +1804,7 @@ bool Session::hasUnfinishedTorrents() const
bool Session::hasRunningSeed() const bool Session::hasRunningSeed() const
{ {
return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentHandle *torrent) return std::any_of(m_torrents.begin(), m_torrents.end(), [](const TorrentHandleImpl *torrent)
{ {
return (torrent->isSeed() && !torrent->isPaused()); return (torrent->isSeed() && !torrent->isPaused());
}); });
@@ -1835,7 +1832,7 @@ void Session::banIP(const QString &ip)
// and from the disk, if the corresponding deleteOption is chosen // and from the disk, if the corresponding deleteOption is chosen
bool Session::deleteTorrent(const InfoHash &hash, const DeleteOption deleteOption) bool Session::deleteTorrent(const InfoHash &hash, const DeleteOption deleteOption)
{ {
TorrentHandle *const torrent = m_torrents.take(hash); TorrentHandleImpl *const torrent = m_torrents.take(hash);
if (!torrent) return false; if (!torrent) return false;
qDebug("Deleting torrent with hash: %s", qUtf8Printable(torrent->hash())); qDebug("Deleting torrent with hash: %s", qUtf8Printable(torrent->hash()));
@@ -1920,21 +1917,21 @@ bool Session::cancelLoadMetadata(const InfoHash &hash)
void Session::increaseTorrentsQueuePos(const QVector<InfoHash> &hashes) void Session::increaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
{ {
using ElementType = std::pair<int, TorrentHandle *>; using ElementType = std::pair<int, TorrentHandleImpl *>;
std::priority_queue<ElementType std::priority_queue<ElementType
, std::vector<ElementType> , std::vector<ElementType>
, std::greater<ElementType>> torrentQueue; , std::greater<ElementType>> torrentQueue;
// Sort torrents by queue position // Sort torrents by queue position
for (const InfoHash &infoHash : hashes) { for (const InfoHash &infoHash : hashes) {
TorrentHandle *const torrent = m_torrents.value(infoHash); TorrentHandleImpl *const torrent = m_torrents.value(infoHash);
if (torrent && !torrent->isSeed()) if (torrent && !torrent->isSeed())
torrentQueue.emplace(torrent->queuePosition(), torrent); torrentQueue.emplace(torrent->queuePosition(), torrent);
} }
// Increase torrents queue position (starting with the one in the highest queue position) // Increase torrents queue position (starting with the one in the highest queue position)
while (!torrentQueue.empty()) { while (!torrentQueue.empty()) {
const TorrentHandle *torrent = torrentQueue.top().second; const TorrentHandleImpl *torrent = torrentQueue.top().second;
torrentQueuePositionUp(torrent->nativeHandle()); torrentQueuePositionUp(torrent->nativeHandle());
torrentQueue.pop(); torrentQueue.pop();
} }
@@ -1944,19 +1941,19 @@ void Session::increaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
void Session::decreaseTorrentsQueuePos(const QVector<InfoHash> &hashes) void Session::decreaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
{ {
using ElementType = std::pair<int, TorrentHandle *>; using ElementType = std::pair<int, TorrentHandleImpl *>;
std::priority_queue<ElementType> torrentQueue; std::priority_queue<ElementType> torrentQueue;
// Sort torrents by queue position // Sort torrents by queue position
for (const InfoHash &infoHash : hashes) { for (const InfoHash &infoHash : hashes) {
TorrentHandle *const torrent = m_torrents.value(infoHash); TorrentHandleImpl *const torrent = m_torrents.value(infoHash);
if (torrent && !torrent->isSeed()) if (torrent && !torrent->isSeed())
torrentQueue.emplace(torrent->queuePosition(), torrent); torrentQueue.emplace(torrent->queuePosition(), torrent);
} }
// Decrease torrents queue position (starting with the one in the lowest queue position) // Decrease torrents queue position (starting with the one in the lowest queue position)
while (!torrentQueue.empty()) { while (!torrentQueue.empty()) {
const TorrentHandle *torrent = torrentQueue.top().second; const TorrentHandleImpl *torrent = torrentQueue.top().second;
torrentQueuePositionDown(torrent->nativeHandle()); torrentQueuePositionDown(torrent->nativeHandle());
torrentQueue.pop(); torrentQueue.pop();
} }
@@ -1969,19 +1966,19 @@ void Session::decreaseTorrentsQueuePos(const QVector<InfoHash> &hashes)
void Session::topTorrentsQueuePos(const QVector<InfoHash> &hashes) void Session::topTorrentsQueuePos(const QVector<InfoHash> &hashes)
{ {
using ElementType = std::pair<int, TorrentHandle *>; using ElementType = std::pair<int, TorrentHandleImpl *>;
std::priority_queue<ElementType> torrentQueue; std::priority_queue<ElementType> torrentQueue;
// Sort torrents by queue position // Sort torrents by queue position
for (const InfoHash &infoHash : hashes) { for (const InfoHash &infoHash : hashes) {
TorrentHandle *const torrent = m_torrents.value(infoHash); TorrentHandleImpl *const torrent = m_torrents.value(infoHash);
if (torrent && !torrent->isSeed()) if (torrent && !torrent->isSeed())
torrentQueue.emplace(torrent->queuePosition(), torrent); torrentQueue.emplace(torrent->queuePosition(), torrent);
} }
// Top torrents queue position (starting with the one in the lowest queue position) // Top torrents queue position (starting with the one in the lowest queue position)
while (!torrentQueue.empty()) { while (!torrentQueue.empty()) {
const TorrentHandle *torrent = torrentQueue.top().second; const TorrentHandleImpl *torrent = torrentQueue.top().second;
torrentQueuePositionTop(torrent->nativeHandle()); torrentQueuePositionTop(torrent->nativeHandle());
torrentQueue.pop(); torrentQueue.pop();
} }
@@ -1991,21 +1988,21 @@ void Session::topTorrentsQueuePos(const QVector<InfoHash> &hashes)
void Session::bottomTorrentsQueuePos(const QVector<InfoHash> &hashes) void Session::bottomTorrentsQueuePos(const QVector<InfoHash> &hashes)
{ {
using ElementType = std::pair<int, TorrentHandle *>; using ElementType = std::pair<int, TorrentHandleImpl *>;
std::priority_queue<ElementType std::priority_queue<ElementType
, std::vector<ElementType> , std::vector<ElementType>
, std::greater<ElementType>> torrentQueue; , std::greater<ElementType>> torrentQueue;
// Sort torrents by queue position // Sort torrents by queue position
for (const InfoHash &infoHash : hashes) { for (const InfoHash &infoHash : hashes) {
TorrentHandle *const torrent = m_torrents.value(infoHash); TorrentHandleImpl *const torrent = m_torrents.value(infoHash);
if (torrent && !torrent->isSeed()) if (torrent && !torrent->isSeed())
torrentQueue.emplace(torrent->queuePosition(), torrent); torrentQueue.emplace(torrent->queuePosition(), torrent);
} }
// Bottom torrents queue position (starting with the one in the highest queue position) // Bottom torrents queue position (starting with the one in the highest queue position)
while (!torrentQueue.empty()) { while (!torrentQueue.empty()) {
const TorrentHandle *torrent = torrentQueue.top().second; const TorrentHandleImpl *torrent = torrentQueue.top().second;
torrentQueuePositionBottom(torrent->nativeHandle()); torrentQueuePositionBottom(torrent->nativeHandle());
torrentQueue.pop(); torrentQueue.pop();
} }
@@ -2016,15 +2013,20 @@ void Session::bottomTorrentsQueuePos(const QVector<InfoHash> &hashes)
saveTorrentsQueue(); saveTorrentsQueue();
} }
void Session::handleTorrentSaveResumeDataRequested(const TorrentHandle *torrent) void Session::handleTorrentSaveResumeDataRequested(const TorrentHandleImpl *torrent)
{ {
qDebug("Saving resume data is requested for torrent '%s'...", qUtf8Printable(torrent->name())); qDebug("Saving resume data is requested for torrent '%s'...", qUtf8Printable(torrent->name()));
++m_numResumeData; ++m_numResumeData;
} }
QHash<InfoHash, TorrentHandle *> Session::torrents() const QVector<TorrentHandle *> Session::torrents() const
{ {
return m_torrents; QVector<TorrentHandle *> result;
result.reserve(m_torrents.size());
for (TorrentHandleImpl *torrent : asConst(m_torrents))
result << torrent;
return result;
} }
bool Session::addTorrent(const QString &source, const AddTorrentParams &params) bool Session::addTorrent(const QString &source, const AddTorrentParams &params)
@@ -2325,7 +2327,7 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
if (m_addingTorrents.contains(hash) || m_loadedMetadata.contains(hash)) if (m_addingTorrents.contains(hash) || m_loadedMetadata.contains(hash))
return false; return false;
TorrentHandle *const torrent = m_torrents.value(hash); TorrentHandleImpl *const torrent = m_torrents.value(hash);
if (torrent) { // a duplicate torrent is added if (torrent) { // a duplicate torrent is added
if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate())) if (torrent->isPrivate() || (!fromMagnetUri && torrentInfo.isPrivate()))
return false; return false;
@@ -2511,7 +2513,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri)
return true; return true;
} }
void Session::exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder) void Session::exportTorrentFile(const TorrentHandle *torrent, TorrentExportFolder folder)
{ {
Q_ASSERT(((folder == TorrentExportFolder::Regular) && !torrentExportDirectory().isEmpty()) || Q_ASSERT(((folder == TorrentExportFolder::Regular) && !torrentExportDirectory().isEmpty()) ||
((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty())); ((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty()));
@@ -2537,7 +2539,7 @@ void Session::exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolde
void Session::generateResumeData(const bool final) void Session::generateResumeData(const bool final)
{ {
for (TorrentHandle *const torrent : asConst(m_torrents)) { for (TorrentHandleImpl *const torrent : asConst(m_torrents)) {
if (!torrent->isValid()) continue; if (!torrent->isValid()) continue;
if (!final && !torrent->needSaveResumeData()) continue; if (!final && !torrent->needSaveResumeData()) continue;
@@ -2582,15 +2584,17 @@ void Session::saveResumeData()
void Session::saveTorrentsQueue() void Session::saveTorrentsQueue()
{ {
// store hash in textual representation
QMap<int, QString> queue; // Use QMap since it should be ordered by key QMap<int, QString> queue; // Use QMap since it should be ordered by key
for (const TorrentHandle *torrent : asConst(torrents())) { for (const TorrentHandleImpl *torrent : asConst(m_torrents)) {
// We require actual (non-cached) queue position here! // We require actual (non-cached) queue position here!
const int queuePos = LTUnderlyingType<LTQueuePosition> {torrent->nativeHandle().queue_position()}; const int queuePos = static_cast<LTUnderlyingType<LTQueuePosition>>(torrent->nativeHandle().queue_position());
if (queuePos >= 0) if (queuePos >= 0)
queue[queuePos] = torrent->hash(); queue[queuePos] = torrent->hash();
} }
QByteArray data; QByteArray data;
data.reserve(((InfoHash::length() * 2) + 1) * queue.size());
for (const QString &hash : asConst(queue)) for (const QString &hash : asConst(queue))
data += (hash.toLatin1() + '\n'); data += (hash.toLatin1() + '\n');
@@ -2623,10 +2627,10 @@ void Session::setDefaultSavePath(QString path)
m_defaultSavePath = path; m_defaultSavePath = path;
if (isDisableAutoTMMWhenDefaultSavePathChanged()) if (isDisableAutoTMMWhenDefaultSavePathChanged())
for (TorrentHandle *const torrent : asConst(torrents())) for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->setAutoTMMEnabled(false); torrent->setAutoTMMEnabled(false);
else else
for (TorrentHandle *const torrent : asConst(torrents())) for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->handleCategorySavePathChanged(); torrent->handleCategorySavePathChanged();
} }
@@ -2637,7 +2641,7 @@ void Session::setTempPath(QString path)
m_tempPath = path; m_tempPath = path;
for (TorrentHandle *const torrent : asConst(m_torrents)) for (TorrentHandleImpl *const torrent : asConst(m_torrents))
torrent->handleTempPathChanged(); torrent->handleTempPathChanged();
} }
@@ -2673,7 +2677,11 @@ QStringList Session::getListeningIPs() const
if (!ifaceAddr.isEmpty() && !allIPv4 && !allIPv6 && configuredAddr.isNull()) { if (!ifaceAddr.isEmpty() && !allIPv4 && !allIPv6 && configuredAddr.isNull()) {
LogMsg(tr("Configured network interface address %1 isn't valid.", "Configured network interface address 124.5.158.1 isn't valid.").arg(ifaceAddr), Log::CRITICAL); LogMsg(tr("Configured network interface address %1 isn't valid.", "Configured network interface address 124.5.158.1 isn't valid.").arg(ifaceAddr), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data. // Pass the invalid user configured interface name/address to libtorrent
// in hopes that it will come online later.
// This will not cause IP leak but allow user to reconnect the interface
// and re-establish connection without restarting the client.
IPs.append(ifaceAddr);
return IPs; return IPs;
} }
@@ -2709,7 +2717,7 @@ QStringList Session::getListeningIPs() const
LogMsg(tr("Can't find the configured address '%1' to listen on" LogMsg(tr("Can't find the configured address '%1' to listen on"
, "Can't find the configured address '192.168.1.3' to listen on") , "Can't find the configured address '192.168.1.3' to listen on")
.arg(ifaceAddr), Log::CRITICAL); .arg(ifaceAddr), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data. IPs.append(ifaceAddr);
} }
return IPs; return IPs;
@@ -2720,7 +2728,7 @@ QStringList Session::getListeningIPs() const
if (!networkIFace.isValid()) { if (!networkIFace.isValid()) {
qDebug("Invalid network interface: %s", qUtf8Printable(ifaceName)); qDebug("Invalid network interface: %s", qUtf8Printable(ifaceName));
LogMsg(tr("The network interface defined is invalid: %1").arg(ifaceName), Log::CRITICAL); LogMsg(tr("The network interface defined is invalid: %1").arg(ifaceName), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data. IPs.append(ifaceName);
return IPs; return IPs;
} }
@@ -2741,7 +2749,7 @@ QStringList Session::getListeningIPs() const
LogMsg(tr("Can't find the configured address '%1' to listen on" LogMsg(tr("Can't find the configured address '%1' to listen on"
, "Can't find the configured address '192.168.1.3' to listen on") , "Can't find the configured address '192.168.1.3' to listen on")
.arg(ifaceAddr), Log::CRITICAL); .arg(ifaceAddr), Log::CRITICAL);
IPs.append("127.0.0.1"); // Force listening to localhost and avoid accidental connection that will expose user data. IPs.append(ifaceAddr);
} }
return IPs; return IPs;
@@ -3680,19 +3688,6 @@ void Session::setStopTrackerTimeout(const int value)
configureDeferred(); configureDeferred();
} }
bool Session::isSuperSeedingEnabled() const
{
return m_isSuperSeedingEnabled;
}
void Session::setSuperSeedingEnabled(const bool enabled)
{
if (enabled != m_isSuperSeedingEnabled) {
m_isSuperSeedingEnabled = enabled;
configureDeferred();
}
}
int Session::maxConnections() const int Session::maxConnections() const
{ {
return m_maxConnections; return m_maxConnections;
@@ -3825,48 +3820,48 @@ void Session::updateSeedingLimitTimer()
} }
} }
void Session::handleTorrentShareLimitChanged(TorrentHandle *const torrent) void Session::handleTorrentShareLimitChanged(TorrentHandleImpl *const torrent)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
updateSeedingLimitTimer(); updateSeedingLimitTimer();
} }
void Session::handleTorrentNameChanged(TorrentHandle *const torrent) void Session::handleTorrentNameChanged(TorrentHandleImpl *const torrent)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
} }
void Session::handleTorrentSavePathChanged(TorrentHandle *const torrent) void Session::handleTorrentSavePathChanged(TorrentHandleImpl *const torrent)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
emit torrentSavePathChanged(torrent); emit torrentSavePathChanged(torrent);
} }
void Session::handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory) void Session::handleTorrentCategoryChanged(TorrentHandleImpl *const torrent, const QString &oldCategory)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
emit torrentCategoryChanged(torrent, oldCategory); emit torrentCategoryChanged(torrent, oldCategory);
} }
void Session::handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag) void Session::handleTorrentTagAdded(TorrentHandleImpl *const torrent, const QString &tag)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
emit torrentTagAdded(torrent, tag); emit torrentTagAdded(torrent, tag);
} }
void Session::handleTorrentTagRemoved(TorrentHandle *const torrent, const QString &tag) void Session::handleTorrentTagRemoved(TorrentHandleImpl *const torrent, const QString &tag)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
emit torrentTagRemoved(torrent, tag); emit torrentTagRemoved(torrent, tag);
} }
void Session::handleTorrentSavingModeChanged(TorrentHandle *const torrent) void Session::handleTorrentSavingModeChanged(TorrentHandleImpl *const torrent)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
emit torrentSavingModeChanged(torrent); emit torrentSavingModeChanged(torrent);
} }
void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QVector<TrackerEntry> &newTrackers) void Session::handleTorrentTrackersAdded(TorrentHandleImpl *const torrent, const QVector<TrackerEntry> &newTrackers)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
@@ -3878,7 +3873,7 @@ void Session::handleTorrentTrackersAdded(TorrentHandle *const torrent, const QVe
emit trackersChanged(torrent); emit trackersChanged(torrent);
} }
void Session::handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QVector<TrackerEntry> &deletedTrackers) void Session::handleTorrentTrackersRemoved(TorrentHandleImpl *const torrent, const QVector<TrackerEntry> &deletedTrackers)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
@@ -3890,27 +3885,27 @@ void Session::handleTorrentTrackersRemoved(TorrentHandle *const torrent, const Q
emit trackersChanged(torrent); emit trackersChanged(torrent);
} }
void Session::handleTorrentTrackersChanged(TorrentHandle *const torrent) void Session::handleTorrentTrackersChanged(TorrentHandleImpl *const torrent)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
emit trackersChanged(torrent); emit trackersChanged(torrent);
} }
void Session::handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QVector<QUrl> &newUrlSeeds) void Session::handleTorrentUrlSeedsAdded(TorrentHandleImpl *const torrent, const QVector<QUrl> &newUrlSeeds)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
for (const QUrl &newUrlSeed : newUrlSeeds) for (const QUrl &newUrlSeed : newUrlSeeds)
LogMsg(tr("URL seed '%1' was added to torrent '%2'").arg(newUrlSeed.toString(), torrent->name())); LogMsg(tr("URL seed '%1' was added to torrent '%2'").arg(newUrlSeed.toString(), torrent->name()));
} }
void Session::handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QVector<QUrl> &urlSeeds) void Session::handleTorrentUrlSeedsRemoved(TorrentHandleImpl *const torrent, const QVector<QUrl> &urlSeeds)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
for (const QUrl &urlSeed : urlSeeds) for (const QUrl &urlSeed : urlSeeds)
LogMsg(tr("URL seed '%1' was removed from torrent '%2'").arg(urlSeed.toString(), torrent->name())); LogMsg(tr("URL seed '%1' was removed from torrent '%2'").arg(urlSeed.toString(), torrent->name()));
} }
void Session::handleTorrentMetadataReceived(TorrentHandle *const torrent) void Session::handleTorrentMetadataReceived(TorrentHandleImpl *const torrent)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
@@ -3931,25 +3926,25 @@ void Session::handleTorrentMetadataReceived(TorrentHandle *const torrent)
emit torrentMetadataLoaded(torrent); emit torrentMetadataLoaded(torrent);
} }
void Session::handleTorrentPaused(TorrentHandle *const torrent) void Session::handleTorrentPaused(TorrentHandleImpl *const torrent)
{ {
if (!torrent->hasError() && !torrent->hasMissingFiles()) if (!torrent->hasError() && !torrent->hasMissingFiles())
torrent->saveResumeData(); torrent->saveResumeData();
emit torrentPaused(torrent); emit torrentPaused(torrent);
} }
void Session::handleTorrentResumed(TorrentHandle *const torrent) void Session::handleTorrentResumed(TorrentHandleImpl *const torrent)
{ {
torrent->saveResumeData(); torrent->saveResumeData();
emit torrentResumed(torrent); emit torrentResumed(torrent);
} }
void Session::handleTorrentChecked(TorrentHandle *const torrent) void Session::handleTorrentChecked(TorrentHandleImpl *const torrent)
{ {
emit torrentFinishedChecking(torrent); emit torrentFinishedChecking(torrent);
} }
void Session::handleTorrentFinished(TorrentHandle *const torrent) void Session::handleTorrentFinished(TorrentHandleImpl *const torrent)
{ {
if (!torrent->hasError() && !torrent->hasMissingFiles()) if (!torrent->hasError() && !torrent->hasMissingFiles())
torrent->saveResumeData(); torrent->saveResumeData();
@@ -3984,45 +3979,40 @@ void Session::handleTorrentFinished(TorrentHandle *const torrent)
emit allTorrentsFinished(); emit allTorrentsFinished();
} }
void Session::handleTorrentResumeDataReady(TorrentHandle *const torrent, const lt::entry &data) void Session::handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, const std::shared_ptr<lt::entry> &data)
{ {
--m_numResumeData; --m_numResumeData;
// Separated thread is used for the blocking IO which results in slow processing of many torrents. // Separated thread is used for the blocking IO which results in slow processing of many torrents.
// Encoding data in parallel while doing IO saves time. Copying lt::entry objects around // Copying lt::entry objects around isn't cheap.
// isn't cheap too.
QByteArray out;
out.reserve(1024 * 1024); // most fastresume file sizes are under 1 MB
lt::bencode(std::back_inserter(out), data);
const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->hash()); const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->hash());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QMetaObject::invokeMethod(m_resumeDataSavingManager QMetaObject::invokeMethod(m_resumeDataSavingManager
, [this, filename, out]() { m_resumeDataSavingManager->save(filename, out); }); , [this, filename, data]() { m_resumeDataSavingManager->save(filename, data); });
#else #else
QMetaObject::invokeMethod(m_resumeDataSavingManager, "save", QMetaObject::invokeMethod(m_resumeDataSavingManager, "save"
Q_ARG(QString, filename), Q_ARG(QByteArray, out)); , Q_ARG(QString, filename), Q_ARG(std::shared_ptr<lt::entry>, data));
#endif #endif
} }
void Session::handleTorrentResumeDataFailed(TorrentHandle *const torrent) void Session::handleTorrentResumeDataFailed(TorrentHandleImpl *const torrent)
{ {
Q_UNUSED(torrent) Q_UNUSED(torrent)
--m_numResumeData; --m_numResumeData;
} }
void Session::handleTorrentTrackerReply(TorrentHandle *const torrent, const QString &trackerUrl) void Session::handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl)
{ {
emit trackerSuccess(torrent, trackerUrl); emit trackerSuccess(torrent, trackerUrl);
} }
void Session::handleTorrentTrackerError(TorrentHandle *const torrent, const QString &trackerUrl) void Session::handleTorrentTrackerError(TorrentHandleImpl *const torrent, const QString &trackerUrl)
{ {
emit trackerError(torrent, trackerUrl); emit trackerError(torrent, trackerUrl);
} }
bool Session::addMoveTorrentStorageJob(TorrentHandle *torrent, const QString &newPath, const MoveStorageMode mode) bool Session::addMoveTorrentStorageJob(TorrentHandleImpl *torrent, const QString &newPath, const MoveStorageMode mode)
{ {
Q_ASSERT(torrent); Q_ASSERT(torrent);
@@ -4095,14 +4085,14 @@ void Session::handleMoveTorrentStorageJobFinished(const QString &errorMessage)
} }
} }
void Session::handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl) void Session::handleTorrentTrackerWarning(TorrentHandleImpl *const torrent, const QString &trackerUrl)
{ {
emit trackerWarning(torrent, trackerUrl); emit trackerWarning(torrent, trackerUrl);
} }
bool Session::hasPerTorrentRatioLimit() const bool Session::hasPerTorrentRatioLimit() const
{ {
return std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentHandle *torrent) return std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentHandleImpl *torrent)
{ {
return (torrent->ratioLimit() >= 0); return (torrent->ratioLimit() >= 0);
}); });
@@ -4110,7 +4100,7 @@ bool Session::hasPerTorrentRatioLimit() const
bool Session::hasPerTorrentSeedingTimeLimit() const bool Session::hasPerTorrentSeedingTimeLimit() const
{ {
return std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentHandle *torrent) return std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentHandleImpl *torrent)
{ {
return (torrent->seedingTimeLimit() >= 0); return (torrent->seedingTimeLimit() >= 0);
}); });
@@ -4184,7 +4174,7 @@ void Session::disableIPFilter()
void Session::recursiveTorrentDownload(const InfoHash &hash) void Session::recursiveTorrentDownload(const InfoHash &hash)
{ {
TorrentHandle *const torrent = m_torrents.value(hash); TorrentHandleImpl *const torrent = m_torrents.value(hash);
if (!torrent) return; if (!torrent) return;
for (int i = 0; i < torrent->filesCount(); ++i) { for (int i = 0; i < torrent->filesCount(); ++i) {
@@ -4494,7 +4484,7 @@ void Session::handleAlert(const lt::alert *a)
void Session::dispatchTorrentAlert(const lt::alert *a) void Session::dispatchTorrentAlert(const lt::alert *a)
{ {
TorrentHandle *const torrent = m_torrents.value(static_cast<const lt::torrent_alert*>(a)->handle.info_hash()); TorrentHandleImpl *const torrent = m_torrents.value(static_cast<const lt::torrent_alert*>(a)->handle.info_hash());
if (torrent) { if (torrent) {
torrent->handleAlert(a); torrent->handleAlert(a);
return; return;
@@ -4514,7 +4504,7 @@ void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle)
const CreateTorrentParams params = m_addingTorrents.take(nativeHandle.info_hash()); const CreateTorrentParams params = m_addingTorrents.take(nativeHandle.info_hash());
TorrentHandle *const torrent = new TorrentHandle(this, nativeHandle, params); TorrentHandleImpl *const torrent = new TorrentHandleImpl(this, nativeHandle, params);
m_torrents.insert(torrent->hash(), torrent); m_torrents.insert(torrent->hash(), torrent);
const bool fromMagnetUri = !torrent->hasMetadata(); const bool fromMagnetUri = !torrent->hasMetadata();
@@ -4658,7 +4648,7 @@ void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
void Session::handleFileErrorAlert(const lt::file_error_alert *p) void Session::handleFileErrorAlert(const lt::file_error_alert *p)
{ {
TorrentHandle *const torrent = m_torrents.value(p->handle.info_hash()); TorrentHandleImpl *const torrent = m_torrents.value(p->handle.info_hash());
if (!torrent) if (!torrent)
return; return;
@@ -4752,7 +4742,7 @@ void Session::handlePeerBanAlert(const lt::peer_ban_alert *p)
void Session::handleUrlSeedAlert(const lt::url_seed_alert *p) void Session::handleUrlSeedAlert(const lt::url_seed_alert *p)
{ {
const TorrentHandle *torrent = m_torrents.value(p->handle.info_hash()); const TorrentHandleImpl *torrent = m_torrents.value(p->handle.info_hash());
if (!torrent) if (!torrent)
return; return;
@@ -4983,7 +4973,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
{ {
if (m_moveStorageQueue.isEmpty()) return; if (m_moveStorageQueue.isEmpty()) return;
const TorrentHandle *torrent = m_torrents.value(p->handle.info_hash()); const TorrentHandleImpl *torrent = m_torrents.value(p->handle.info_hash());
const MoveStorageJob &currentJob = m_moveStorageQueue.first(); const MoveStorageJob &currentJob = m_moveStorageQueue.first();
if (currentJob.torrent != torrent) return; if (currentJob.torrent != torrent) return;
@@ -4995,7 +4985,7 @@ void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert
{ {
if (m_moveStorageQueue.isEmpty()) return; if (m_moveStorageQueue.isEmpty()) return;
const TorrentHandle *torrent = m_torrents.value(p->handle.info_hash()); const TorrentHandleImpl *torrent = m_torrents.value(p->handle.info_hash());
const MoveStorageJob &currentJob = m_moveStorageQueue.first(); const MoveStorageJob &currentJob = m_moveStorageQueue.first();
if (currentJob.torrent != torrent) return; if (currentJob.torrent != torrent) return;
@@ -5008,7 +4998,7 @@ void Session::handleStateUpdateAlert(const lt::state_update_alert *p)
updatedTorrents.reserve(p->status.size()); updatedTorrents.reserve(p->status.size());
for (const lt::torrent_status &status : p->status) { for (const lt::torrent_status &status : p->status) {
TorrentHandle *const torrent = m_torrents.value(status.info_hash); TorrentHandleImpl *const torrent = m_torrents.value(status.info_hash);
if (!torrent) if (!torrent)
continue; continue;

View File

@@ -30,6 +30,7 @@
#ifndef BITTORRENT_SESSION_H #ifndef BITTORRENT_SESSION_H
#define BITTORRENT_SESSION_H #define BITTORRENT_SESSION_H
#include <memory>
#include <vector> #include <vector>
#include <libtorrent/fwd.hpp> #include <libtorrent/fwd.hpp>
@@ -92,6 +93,7 @@ namespace BitTorrent
class InfoHash; class InfoHash;
class MagnetUri; class MagnetUri;
class TorrentHandle; class TorrentHandle;
class TorrentHandleImpl;
class Tracker; class Tracker;
class TrackerEntry; class TrackerEntry;
struct CreateTorrentParams; struct CreateTorrentParams;
@@ -377,8 +379,6 @@ namespace BitTorrent
void setAnnounceIP(const QString &ip); void setAnnounceIP(const QString &ip);
int stopTrackerTimeout() const; int stopTrackerTimeout() const;
void setStopTrackerTimeout(int value); void setStopTrackerTimeout(int value);
bool isSuperSeedingEnabled() const;
void setSuperSeedingEnabled(bool enabled);
int maxConnections() const; int maxConnections() const;
void setMaxConnections(int max); void setMaxConnections(int max);
int maxConnectionsPerTorrent() const; int maxConnectionsPerTorrent() const;
@@ -412,7 +412,7 @@ namespace BitTorrent
void startUpTorrents(); void startUpTorrents();
TorrentHandle *findTorrent(const InfoHash &hash) const; TorrentHandle *findTorrent(const InfoHash &hash) const;
QHash<InfoHash, TorrentHandle *> torrents() const; QVector<TorrentHandle *> torrents() const;
bool hasActiveTorrents() const; bool hasActiveTorrents() const;
bool hasUnfinishedTorrents() const; bool hasUnfinishedTorrents() const;
bool hasRunningSeed() const; bool hasRunningSeed() const;
@@ -441,31 +441,31 @@ namespace BitTorrent
void bottomTorrentsQueuePos(const QVector<InfoHash> &hashes); void bottomTorrentsQueuePos(const QVector<InfoHash> &hashes);
// TorrentHandle interface // TorrentHandle interface
void handleTorrentSaveResumeDataRequested(const TorrentHandle *torrent); void handleTorrentSaveResumeDataRequested(const TorrentHandleImpl *torrent);
void handleTorrentShareLimitChanged(TorrentHandle *const torrent); void handleTorrentShareLimitChanged(TorrentHandleImpl *const torrent);
void handleTorrentNameChanged(TorrentHandle *const torrent); void handleTorrentNameChanged(TorrentHandleImpl *const torrent);
void handleTorrentSavePathChanged(TorrentHandle *const torrent); void handleTorrentSavePathChanged(TorrentHandleImpl *const torrent);
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory); void handleTorrentCategoryChanged(TorrentHandleImpl *const torrent, const QString &oldCategory);
void handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag); void handleTorrentTagAdded(TorrentHandleImpl *const torrent, const QString &tag);
void handleTorrentTagRemoved(TorrentHandle *const torrent, const QString &tag); void handleTorrentTagRemoved(TorrentHandleImpl *const torrent, const QString &tag);
void handleTorrentSavingModeChanged(TorrentHandle *const torrent); void handleTorrentSavingModeChanged(TorrentHandleImpl *const torrent);
void handleTorrentMetadataReceived(TorrentHandle *const torrent); void handleTorrentMetadataReceived(TorrentHandleImpl *const torrent);
void handleTorrentPaused(TorrentHandle *const torrent); void handleTorrentPaused(TorrentHandleImpl *const torrent);
void handleTorrentResumed(TorrentHandle *const torrent); void handleTorrentResumed(TorrentHandleImpl *const torrent);
void handleTorrentChecked(TorrentHandle *const torrent); void handleTorrentChecked(TorrentHandleImpl *const torrent);
void handleTorrentFinished(TorrentHandle *const torrent); void handleTorrentFinished(TorrentHandleImpl *const torrent);
void handleTorrentTrackersAdded(TorrentHandle *const torrent, const QVector<TrackerEntry> &newTrackers); void handleTorrentTrackersAdded(TorrentHandleImpl *const torrent, const QVector<TrackerEntry> &newTrackers);
void handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QVector<TrackerEntry> &deletedTrackers); void handleTorrentTrackersRemoved(TorrentHandleImpl *const torrent, const QVector<TrackerEntry> &deletedTrackers);
void handleTorrentTrackersChanged(TorrentHandle *const torrent); void handleTorrentTrackersChanged(TorrentHandleImpl *const torrent);
void handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QVector<QUrl> &newUrlSeeds); void handleTorrentUrlSeedsAdded(TorrentHandleImpl *const torrent, const QVector<QUrl> &newUrlSeeds);
void handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QVector<QUrl> &urlSeeds); void handleTorrentUrlSeedsRemoved(TorrentHandleImpl *const torrent, const QVector<QUrl> &urlSeeds);
void handleTorrentResumeDataReady(TorrentHandle *const torrent, const lt::entry &data); void handleTorrentResumeDataReady(TorrentHandleImpl *const torrent, const std::shared_ptr<lt::entry> &data);
void handleTorrentResumeDataFailed(TorrentHandle *const torrent); void handleTorrentResumeDataFailed(TorrentHandleImpl *const torrent);
void handleTorrentTrackerReply(TorrentHandle *const torrent, const QString &trackerUrl); void handleTorrentTrackerReply(TorrentHandleImpl *const torrent, const QString &trackerUrl);
void handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl); void handleTorrentTrackerWarning(TorrentHandleImpl *const torrent, const QString &trackerUrl);
void handleTorrentTrackerError(TorrentHandle *const torrent, const QString &trackerUrl); void handleTorrentTrackerError(TorrentHandleImpl *const torrent, const QString &trackerUrl);
bool addMoveTorrentStorageJob(TorrentHandle *torrent, const QString &newPath, MoveStorageMode mode); bool addMoveTorrentStorageJob(TorrentHandleImpl *torrent, const QString &newPath, MoveStorageMode mode);
signals: signals:
void addTorrentFailed(const QString &error); void addTorrentFailed(const QString &error);
@@ -522,7 +522,7 @@ namespace BitTorrent
private: private:
struct MoveStorageJob struct MoveStorageJob
{ {
TorrentHandle *torrent; TorrentHandleImpl *torrent;
QString path; QString path;
MoveStorageMode mode; MoveStorageMode mode;
}; };
@@ -572,7 +572,7 @@ namespace BitTorrent
bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const; bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
void updateSeedingLimitTimer(); void updateSeedingLimitTimer();
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular); void exportTorrentFile(const TorrentHandle *torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
void handleAlert(const lt::alert *a); void handleAlert(const lt::alert *a);
void dispatchTorrentAlert(const lt::alert *a); void dispatchTorrentAlert(const lt::alert *a);
@@ -657,7 +657,6 @@ namespace BitTorrent
CachedSettingValue<bool> m_includeOverheadInLimits; CachedSettingValue<bool> m_includeOverheadInLimits;
CachedSettingValue<QString> m_announceIP; CachedSettingValue<QString> m_announceIP;
CachedSettingValue<int> m_stopTrackerTimeout; CachedSettingValue<int> m_stopTrackerTimeout;
CachedSettingValue<bool> m_isSuperSeedingEnabled;
CachedSettingValue<int> m_maxConnections; CachedSettingValue<int> m_maxConnections;
CachedSettingValue<int> m_maxUploads; CachedSettingValue<int> m_maxUploads;
CachedSettingValue<int> m_maxConnectionsPerTorrent; CachedSettingValue<int> m_maxConnectionsPerTorrent;
@@ -735,7 +734,7 @@ namespace BitTorrent
ResumeDataSavingManager *m_resumeDataSavingManager = nullptr; ResumeDataSavingManager *m_resumeDataSavingManager = nullptr;
QHash<InfoHash, TorrentInfo> m_loadedMetadata; QHash<InfoHash, TorrentInfo> m_loadedMetadata;
QHash<InfoHash, TorrentHandle *> m_torrents; QHash<InfoHash, TorrentHandleImpl *> m_torrents;
QHash<InfoHash, CreateTorrentParams> m_addingTorrents; QHash<InfoHash, CreateTorrentParams> m_addingTorrents;
QHash<QString, AddTorrentParams> m_downloadedTorrents; QHash<QString, AddTorrentParams> m_downloadedTorrents;
QHash<InfoHash, RemovingTorrentData> m_removingTorrents; QHash<InfoHash, RemovingTorrentData> m_removingTorrents;
@@ -760,4 +759,9 @@ namespace BitTorrent
}; };
} }
#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0))
Q_DECLARE_METATYPE(std::shared_ptr<lt::entry>)
const int sharedPtrLtEntryTypeID = qRegisterMetaType<std::shared_ptr<lt::entry>>();
#endif
#endif // BITTORRENT_SESSION_H #endif // BITTORRENT_SESSION_H

View File

@@ -41,8 +41,10 @@
#include <QFileInfo> #include <QFileInfo>
#include <QHash> #include <QHash>
#include "base/exceptions.h"
#include "base/global.h" #include "base/global.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/io.h"
#include "base/utils/string.h" #include "base/utils/string.h"
#include "private/ltunderlyingtype.h" #include "private/ltunderlyingtype.h"
@@ -162,7 +164,7 @@ void TorrentCreatorThread::run()
lt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString() lt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
, [this, &newTorrent](const LTPieceIndex n) , [this, &newTorrent](const LTPieceIndex n)
{ {
sendProgressSignal(LTUnderlyingType<LTPieceIndex> {n}, newTorrent.num_pieces()); sendProgressSignal(static_cast<LTUnderlyingType<LTPieceIndex>>(n), newTorrent.num_pieces());
}); });
// Set qBittorrent as creator and add user comment to // Set qBittorrent as creator and add user comment to
// torrent_info structure // torrent_info structure
@@ -182,19 +184,19 @@ void TorrentCreatorThread::run()
if (isInterruptionRequested()) return; if (isInterruptionRequested()) return;
// create the torrent // create the torrent
std::ofstream outfile( QFile outfile {m_params.savePath};
#ifdef _MSC_VER if (!outfile.open(QIODevice::WriteOnly)) {
Utils::Fs::toNativePath(m_params.savePath).toStdWString().c_str() throw RuntimeError {tr("Create new torrent file failed. Reason: %1")
#else .arg(outfile.errorString())};
Utils::Fs::toNativePath(m_params.savePath).toUtf8().constData() }
#endif
, (std::ios_base::out | std::ios_base::binary | std::ios_base::trunc));
if (outfile.fail())
throw std::runtime_error(tr("create new torrent file failed").toStdString());
if (isInterruptionRequested()) return; if (isInterruptionRequested()) return;
lt::bencode(std::ostream_iterator<char>(outfile), entry); lt::bencode(Utils::IO::FileDeviceOutputIterator {outfile}, entry);
if (outfile.error() != QFileDevice::NoError) {
throw RuntimeError {tr("Create new torrent file failed. Reason: %1")
.arg(outfile.errorString())};
}
outfile.close(); outfile.close();
emit updateProgress(100); emit updateProgress(100);

View File

@@ -48,7 +48,7 @@ namespace BitTorrent
QStringList urlSeeds; QStringList urlSeeds;
}; };
class TorrentCreatorThread : public QThread class TorrentCreatorThread final : public QThread
{ {
Q_OBJECT Q_OBJECT

File diff suppressed because it is too large Load Diff

View File

@@ -27,84 +27,30 @@
* exception statement from your version. * exception statement from your version.
*/ */
#ifndef BITTORRENT_TORRENTHANDLE_H #pragma once
#define BITTORRENT_TORRENTHANDLE_H
#include <functional>
#include <libtorrent/fwd.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_status.hpp>
#include <QDateTime>
#include <QHash> #include <QHash>
#include <QObject> #include <QMetaType>
#include <QQueue>
#include <QSet> #include <QSet>
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include "private/speedmonitor.h"
#include "infohash.h"
#include "torrentinfo.h"
extern const QString QB_EXT;
class QBitArray; class QBitArray;
class QDateTime; class QDateTime;
class QStringList; class QStringList;
class QUrl; class QUrl;
extern const QString QB_EXT;
namespace BitTorrent namespace BitTorrent
{ {
enum class DownloadPriority; enum class DownloadPriority;
class InfoHash;
class PeerInfo; class PeerInfo;
class Session; class TorrentInfo;
class TrackerEntry; class TrackerEntry;
struct AddTorrentParams;
struct PeerAddress; struct PeerAddress;
struct CreateTorrentParams
{
bool restored; // is existing torrent job?
// for both new and restored torrents
QString name;
QString category;
QSet<QString> tags;
QString savePath;
bool disableTempPath;
bool sequential;
bool firstLastPiecePriority;
bool hasSeedStatus;
bool skipChecking;
bool hasRootFolder;
bool forced;
bool paused;
int uploadLimit;
int downloadLimit;
// for new torrents
QVector<DownloadPriority> filePriorities;
QDateTime addedTime;
// for restored torrents
qreal ratioLimit;
int seedingTimeLimit;
CreateTorrentParams();
explicit CreateTorrentParams(const AddTorrentParams &params);
};
struct TrackerInfo
{
QString lastMessage;
int numPeers = 0;
};
enum class MoveStorageMode
{
KeepExistingFiles,
Overwrite
};
enum class TorrentState enum class TorrentState
{ {
Unknown = -1, Unknown = -1,
@@ -135,13 +81,16 @@ namespace BitTorrent
Error Error
}; };
struct TrackerInfo
{
QString lastMessage;
int numPeers = 0;
};
uint qHash(TorrentState key, uint seed); uint qHash(TorrentState key, uint seed);
class TorrentHandle : public QObject class TorrentHandle
{ {
Q_DISABLE_COPY(TorrentHandle)
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentHandle)
public: public:
static const qreal USE_GLOBAL_RATIO; static const qreal USE_GLOBAL_RATIO;
static const qreal NO_RATIO_LIMIT; static const qreal NO_RATIO_LIMIT;
@@ -152,24 +101,21 @@ namespace BitTorrent
static const qreal MAX_RATIO; static const qreal MAX_RATIO;
static const int MAX_SEEDING_TIME; static const int MAX_SEEDING_TIME;
TorrentHandle(Session *session, const lt::torrent_handle &nativeHandle, virtual ~TorrentHandle() = default;
const CreateTorrentParams &params);
~TorrentHandle();
bool isValid() const; virtual InfoHash hash() const = 0;
InfoHash hash() const; virtual QString name() const = 0;
QString name() const; virtual QDateTime creationDate() const = 0;
QDateTime creationDate() const; virtual QString creator() const = 0;
QString creator() const; virtual QString comment() const = 0;
QString comment() const; virtual bool isPrivate() const = 0;
bool isPrivate() const; virtual qlonglong totalSize() const = 0;
qlonglong totalSize() const; virtual qlonglong wantedSize() const = 0;
qlonglong wantedSize() const; virtual qlonglong completedSize() const = 0;
qlonglong completedSize() const; virtual qlonglong incompletedSize() const = 0;
qlonglong incompletedSize() const; virtual qlonglong pieceLength() const = 0;
qlonglong pieceLength() const; virtual qlonglong wastedSize() const = 0;
qlonglong wastedSize() const; virtual QString currentTracker() const = 0;
QString currentTracker() const;
// 1. savePath() - the path where all the files and subfolders of torrent are stored (as always). // 1. savePath() - the path where all the files and subfolders of torrent are stored (as always).
// 2. rootPath() - absolute path of torrent file tree (save path + first item from 1st torrent file path). // 2. rootPath() - absolute path of torrent file tree (save path + first item from 1st torrent file path).
@@ -214,237 +160,140 @@ namespace BitTorrent
// | B | /home/user/torrents/torrentB | /home/user/torrents/torrentB/subdir1/file1 | // | B | /home/user/torrents/torrentB | /home/user/torrents/torrentB/subdir1/file1 |
// | C | /home/user/torrents/file1 | /home/user/torrents/file1 | // | C | /home/user/torrents/file1 | /home/user/torrents/file1 |
QString savePath(bool actual = false) const; virtual QString savePath(bool actual = false) const = 0;
QString rootPath(bool actual = false) const; virtual QString rootPath(bool actual = false) const = 0;
QString contentPath(bool actual = false) const; virtual QString contentPath(bool actual = false) const = 0;
bool useTempPath() const; virtual bool useTempPath() const = 0;
bool isAutoTMMEnabled() const; virtual bool isAutoTMMEnabled() const = 0;
void setAutoTMMEnabled(bool enabled); virtual void setAutoTMMEnabled(bool enabled) = 0;
QString category() const; virtual QString category() const = 0;
bool belongsToCategory(const QString &category) const; virtual bool belongsToCategory(const QString &category) const = 0;
bool setCategory(const QString &category); virtual bool setCategory(const QString &category) = 0;
QSet<QString> tags() const; virtual QSet<QString> tags() const = 0;
bool hasTag(const QString &tag) const; virtual bool hasTag(const QString &tag) const = 0;
bool addTag(const QString &tag); virtual bool addTag(const QString &tag) = 0;
bool removeTag(const QString &tag); virtual bool removeTag(const QString &tag) = 0;
void removeAllTags(); virtual void removeAllTags() = 0;
bool hasRootFolder() const; virtual bool hasRootFolder() const = 0;
int filesCount() const; virtual int filesCount() const = 0;
int piecesCount() const; virtual int piecesCount() const = 0;
int piecesHave() const; virtual int piecesHave() const = 0;
qreal progress() const; virtual qreal progress() const = 0;
QDateTime addedTime() const; virtual QDateTime addedTime() const = 0;
qreal ratioLimit() const; virtual qreal ratioLimit() const = 0;
int seedingTimeLimit() const; virtual int seedingTimeLimit() const = 0;
QString filePath(int index) const; virtual QString filePath(int index) const = 0;
QString fileName(int index) const; virtual QString fileName(int index) const = 0;
qlonglong fileSize(int index) const; virtual qlonglong fileSize(int index) const = 0;
QStringList absoluteFilePaths() const; virtual QStringList absoluteFilePaths() const = 0;
QStringList absoluteFilePathsUnwanted() const; virtual QStringList absoluteFilePathsUnwanted() const = 0;
QVector<DownloadPriority> filePriorities() const; virtual QVector<DownloadPriority> filePriorities() const = 0;
TorrentInfo info() const;
bool isSeed() const;
bool isPaused() const;
bool isResumed() const;
bool isQueued() const;
bool isForced() const;
bool isChecking() const;
bool isDownloading() const;
bool isUploading() const;
bool isCompleted() const;
bool isActive() const;
bool isInactive() const;
bool isErrored() const;
bool isSequentialDownload() const;
bool hasFirstLastPiecePriority() const;
TorrentState state() const;
bool hasMetadata() const;
bool hasMissingFiles() const;
bool hasError() const;
bool hasFilteredPieces() const;
int queuePosition() const;
QVector<TrackerEntry> trackers() const;
QHash<QString, TrackerInfo> trackerInfos() const;
QVector<QUrl> urlSeeds() const;
QString error() const;
qlonglong totalDownload() const;
qlonglong totalUpload() const;
qlonglong activeTime() const;
qlonglong finishedTime() const;
qlonglong seedingTime() const;
qlonglong eta() const;
QVector<qreal> filesProgress() const;
int seedsCount() const;
int peersCount() const;
int leechsCount() const;
int totalSeedsCount() const;
int totalPeersCount() const;
int totalLeechersCount() const;
int completeCount() const;
int incompleteCount() const;
QDateTime lastSeenComplete() const;
QDateTime completedTime() const;
qlonglong timeSinceUpload() const;
qlonglong timeSinceDownload() const;
qlonglong timeSinceActivity() const;
int downloadLimit() const;
int uploadLimit() const;
bool superSeeding() const;
QVector<PeerInfo> peers() const;
QBitArray pieces() const;
QBitArray downloadingPieces() const;
QVector<int> pieceAvailability() const;
qreal distributedCopies() const;
qreal maxRatio() const;
int maxSeedingTime() const;
qreal realRatio() const;
int uploadPayloadRate() const;
int downloadPayloadRate() const;
qlonglong totalPayloadUpload() const;
qlonglong totalPayloadDownload() const;
int connectionsCount() const;
int connectionsLimit() const;
qlonglong nextAnnounce() const;
void setName(const QString &name);
void setSequentialDownload(bool enable);
void toggleSequentialDownload();
void setFirstLastPiecePriority(bool enabled);
void toggleFirstLastPiecePriority();
void pause();
void resume(bool forced = false);
void move(QString path);
void forceReannounce(int index = -1);
void forceDHTAnnounce();
void forceRecheck();
void renameFile(int index, const QString &name);
void prioritizeFiles(const QVector<DownloadPriority> &priorities);
void setRatioLimit(qreal limit);
void setSeedingTimeLimit(int limit);
void setUploadLimit(int limit);
void setDownloadLimit(int limit);
void setSuperSeeding(bool enable);
void flushCache() const;
void addTrackers(const QVector<TrackerEntry> &trackers);
void replaceTrackers(const QVector<TrackerEntry> &trackers);
void addUrlSeeds(const QVector<QUrl> &urlSeeds);
void removeUrlSeeds(const QVector<QUrl> &urlSeeds);
bool connectPeer(const PeerAddress &peerAddress);
QString toMagnetUri() const;
bool needSaveResumeData() const;
// Session interface
lt::torrent_handle nativeHandle() const;
void handleAlert(const lt::alert *a);
void handleStateUpdate(const lt::torrent_status &nativeStatus);
void handleTempPathChanged();
void handleCategorySavePathChanged();
void handleAppendExtensionToggled();
void saveResumeData();
void handleStorageMoved(const QString &newPath, const QString &errorMessage);
virtual TorrentInfo info() const = 0;
virtual bool isSeed() const = 0;
virtual bool isPaused() const = 0;
virtual bool isResumed() const = 0;
virtual bool isQueued() const = 0;
virtual bool isForced() const = 0;
virtual bool isChecking() const = 0;
virtual bool isDownloading() const = 0;
virtual bool isUploading() const = 0;
virtual bool isCompleted() const = 0;
virtual bool isActive() const = 0;
virtual bool isInactive() const = 0;
virtual bool isErrored() const = 0;
virtual bool isSequentialDownload() const = 0;
virtual bool hasFirstLastPiecePriority() const = 0;
virtual TorrentState state() const = 0;
virtual bool hasMetadata() const = 0;
virtual bool hasMissingFiles() const = 0;
virtual bool hasError() const = 0;
virtual bool hasFilteredPieces() const = 0;
virtual int queuePosition() const = 0;
virtual QVector<TrackerEntry> trackers() const = 0;
virtual QHash<QString, TrackerInfo> trackerInfos() const = 0;
virtual QVector<QUrl> urlSeeds() const = 0;
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 seedingTime() const = 0;
virtual qlonglong eta() const = 0;
virtual QVector<qreal> filesProgress() const = 0;
virtual int seedsCount() const = 0;
virtual int peersCount() const = 0;
virtual int leechsCount() const = 0;
virtual int totalSeedsCount() const = 0;
virtual int totalPeersCount() const = 0;
virtual int totalLeechersCount() const = 0;
virtual int completeCount() const = 0;
virtual int incompleteCount() 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;
virtual QVector<PeerInfo> peers() const = 0;
virtual QBitArray pieces() const = 0;
virtual QBitArray downloadingPieces() const = 0;
virtual QVector<int> pieceAvailability() const = 0;
virtual qreal distributedCopies() const = 0;
virtual qreal maxRatio() const = 0;
virtual int maxSeedingTime() const = 0;
virtual qreal realRatio() const = 0;
virtual int uploadPayloadRate() const = 0;
virtual int downloadPayloadRate() const = 0;
virtual qlonglong totalPayloadUpload() const = 0;
virtual qlonglong totalPayloadDownload() const = 0;
virtual int connectionsCount() const = 0;
virtual int connectionsLimit() const = 0;
virtual qlonglong nextAnnounce() const = 0;
/** /**
* @brief fraction of file pieces that are available at least from one peer * @brief fraction of file pieces that are available at least from one peer
* *
* This is not the same as torrrent availability, it is just a fraction of pieces * This is not the same as torrrent availability, it is just a fraction of pieces
* that can be downloaded right now. It varies between 0 to 1. * that can be downloaded right now. It varies between 0 to 1.
*/ */
QVector<qreal> availableFileFractions() const; virtual QVector<qreal> availableFileFractions() const = 0;
private: virtual void setName(const QString &name) = 0;
typedef std::function<void ()> EventTrigger; virtual void setSequentialDownload(bool enable) = 0;
virtual void setFirstLastPiecePriority(bool enabled) = 0;
virtual void pause() = 0;
virtual void resume(bool forced = false) = 0;
virtual void move(QString path) = 0;
virtual void forceReannounce(int index = -1) = 0;
virtual void forceDHTAnnounce() = 0;
virtual void forceRecheck() = 0;
virtual void renameFile(int index, const QString &name) = 0;
virtual void prioritizeFiles(const QVector<DownloadPriority> &priorities) = 0;
virtual void setRatioLimit(qreal limit) = 0;
virtual void setSeedingTimeLimit(int limit) = 0;
virtual void setUploadLimit(int limit) = 0;
virtual void setDownloadLimit(int limit) = 0;
virtual void setSuperSeeding(bool enable) = 0;
virtual void flushCache() const = 0;
virtual void addTrackers(const QVector<TrackerEntry> &trackers) = 0;
virtual void replaceTrackers(const QVector<TrackerEntry> &trackers) = 0;
virtual void addUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
virtual void removeUrlSeeds(const QVector<QUrl> &urlSeeds) = 0;
virtual bool connectPeer(const PeerAddress &peerAddress) = 0;
#if (LIBTORRENT_VERSION_NUM < 10200) virtual QString createMagnetURI() const = 0;
using LTFileIndex = int;
#else
using LTFileIndex = lt::file_index_t;
#endif
void updateStatus(); void toggleSequentialDownload();
void updateStatus(const lt::torrent_status &nativeStatus); void toggleFirstLastPiecePriority();
void updateState();
void updateTorrentInfo();
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
void handleFileCompletedAlert(const lt::file_completed_alert *p);
void handleFileRenamedAlert(const lt::file_renamed_alert *p);
void handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p);
void handleMetadataReceivedAlert(const lt::metadata_received_alert *p);
void handlePerformanceAlert(const lt::performance_alert *p) const;
void handleSaveResumeDataAlert(const lt::save_resume_data_alert *p);
void handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p);
void handleTorrentCheckedAlert(const lt::torrent_checked_alert *p);
void handleTorrentFinishedAlert(const lt::torrent_finished_alert *p);
void handleTorrentPausedAlert(const lt::torrent_paused_alert *p);
void handleTorrentResumedAlert(const lt::torrent_resumed_alert *p);
void handleTrackerErrorAlert(const lt::tracker_error_alert *p);
void handleTrackerReplyAlert(const lt::tracker_reply_alert *p);
void handleTrackerWarningAlert(const lt::tracker_warning_alert *p);
void resume_impl(bool forced);
bool isMoveInProgress() const;
QString actualStorageLocation() const;
bool isAutoManaged() const;
void setAutoManaged(bool enable);
void adjustActualSavePath();
void adjustActualSavePath_impl();
void move_impl(QString path, MoveStorageMode mode);
void moveStorage(const QString &newPath, MoveStorageMode mode);
void manageIncompleteFiles();
void setFirstLastPiecePriorityImpl(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
Session *const m_session;
lt::torrent_handle m_nativeHandle;
lt::torrent_status m_nativeStatus;
TorrentState m_state;
TorrentInfo m_torrentInfo;
SpeedMonitor m_speedMonitor;
InfoHash m_hash;
bool m_storageIsMoving = false;
// m_moveFinishedTriggers is activated only when the following conditions are met:
// all file rename jobs complete, all file move jobs complete
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
QString m_name;
QString m_savePath;
QString m_category;
QSet<QString> m_tags;
bool m_hasSeedStatus;
qreal m_ratioLimit;
int m_seedingTimeLimit;
bool m_tempPathDisabled;
bool m_fastresumeDataRejected;
bool m_hasMissingFiles;
bool m_hasRootFolder;
bool m_needsToSetFirstLastPiecePriority;
QHash<QString, TrackerInfo> m_trackerInfos;
bool m_unchecked = false;
}; };
} }
Q_DECLARE_METATYPE(BitTorrent::TorrentState) Q_DECLARE_METATYPE(BitTorrent::TorrentState)
#endif // BITTORRENT_TORRENTHANDLE_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,338 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <functional>
#include <libtorrent/fwd.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_status.hpp>
#include <QDateTime>
#include <QHash>
#include <QObject>
#include <QQueue>
#include <QSet>
#include <QString>
#include <QVector>
#include "private/speedmonitor.h"
#include "infohash.h"
#include "torrenthandle.h"
#include "torrentinfo.h"
namespace BitTorrent
{
class Session;
struct AddTorrentParams;
struct CreateTorrentParams
{
CreateTorrentParams() = default;
explicit CreateTorrentParams(const AddTorrentParams &params);
// for both new and restored torrents
QString name;
QString category;
QSet<QString> tags;
QString savePath;
int uploadLimit = -1;
int downloadLimit = -1;
bool disableTempPath = false;
bool sequential = false;
bool firstLastPiecePriority = false;
bool hasSeedStatus = false;
bool skipChecking = false;
bool hasRootFolder = true;
bool forced = false;
bool paused = false;
bool restored = false; // is existing torrent job?
// for new torrents
QVector<DownloadPriority> filePriorities;
QDateTime addedTime;
// for restored torrents
qreal ratioLimit = TorrentHandle::USE_GLOBAL_RATIO;
int seedingTimeLimit = TorrentHandle::USE_GLOBAL_SEEDING_TIME;
};
enum class MoveStorageMode
{
KeepExistingFiles,
Overwrite
};
class TorrentHandleImpl final : public QObject, public TorrentHandle
{
Q_DISABLE_COPY(TorrentHandleImpl)
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentHandleImpl)
public:
TorrentHandleImpl(Session *session, const lt::torrent_handle &nativeHandle,
const CreateTorrentParams &params);
~TorrentHandleImpl() override;
bool isValid() const;
InfoHash hash() const override;
QString name() const override;
QDateTime creationDate() const override;
QString creator() const override;
QString comment() const override;
bool isPrivate() const override;
qlonglong totalSize() const override;
qlonglong wantedSize() const override;
qlonglong completedSize() const override;
qlonglong incompletedSize() const override;
qlonglong pieceLength() const override;
qlonglong wastedSize() const override;
QString currentTracker() const override;
QString savePath(bool actual = false) const override;
QString rootPath(bool actual = false) const override;
QString contentPath(bool actual = false) const override;
bool useTempPath() const override;
bool isAutoTMMEnabled() const override;
void setAutoTMMEnabled(bool enabled) override;
QString category() const override;
bool belongsToCategory(const QString &category) const override;
bool setCategory(const QString &category) override;
QSet<QString> tags() const override;
bool hasTag(const QString &tag) const override;
bool addTag(const QString &tag) override;
bool removeTag(const QString &tag) override;
void removeAllTags() override;
bool hasRootFolder() const override;
int filesCount() const override;
int piecesCount() const override;
int piecesHave() const override;
qreal progress() const override;
QDateTime addedTime() const override;
qreal ratioLimit() const override;
int seedingTimeLimit() const override;
QString filePath(int index) const override;
QString fileName(int index) const override;
qlonglong fileSize(int index) const override;
QStringList absoluteFilePaths() const override;
QStringList absoluteFilePathsUnwanted() const override;
QVector<DownloadPriority> filePriorities() const override;
TorrentInfo info() const override;
bool isSeed() const override;
bool isPaused() const override;
bool isResumed() const override;
bool isQueued() const override;
bool isForced() const override;
bool isChecking() const override;
bool isDownloading() const override;
bool isUploading() const override;
bool isCompleted() const override;
bool isActive() const override;
bool isInactive() const override;
bool isErrored() const override;
bool isSequentialDownload() const override;
bool hasFirstLastPiecePriority() const override;
TorrentState state() const override;
bool hasMetadata() const override;
bool hasMissingFiles() const override;
bool hasError() const override;
bool hasFilteredPieces() const override;
int queuePosition() const override;
QVector<TrackerEntry> trackers() const override;
QHash<QString, TrackerInfo> trackerInfos() const override;
QVector<QUrl> urlSeeds() const override;
QString error() const override;
qlonglong totalDownload() const override;
qlonglong totalUpload() const override;
qlonglong activeTime() const override;
qlonglong finishedTime() const override;
qlonglong seedingTime() const override;
qlonglong eta() const override;
QVector<qreal> filesProgress() const override;
int seedsCount() const override;
int peersCount() const override;
int leechsCount() const override;
int totalSeedsCount() const override;
int totalPeersCount() const override;
int totalLeechersCount() const override;
int completeCount() const override;
int incompleteCount() 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;
QVector<PeerInfo> peers() const override;
QBitArray pieces() const override;
QBitArray downloadingPieces() const override;
QVector<int> pieceAvailability() const override;
qreal distributedCopies() const override;
qreal maxRatio() const override;
int maxSeedingTime() const override;
qreal realRatio() const override;
int uploadPayloadRate() const override;
int downloadPayloadRate() const override;
qlonglong totalPayloadUpload() const override;
qlonglong totalPayloadDownload() const override;
int connectionsCount() const override;
int connectionsLimit() const override;
qlonglong nextAnnounce() const override;
QVector<qreal> availableFileFractions() const override;
void setName(const QString &name) override;
void setSequentialDownload(bool enable) override;
void setFirstLastPiecePriority(bool enabled) override;
void pause() override;
void resume(bool forced = false) override;
void move(QString path) override;
void forceReannounce(int index = -1) override;
void forceDHTAnnounce() override;
void forceRecheck() override;
void renameFile(int index, const QString &name) override;
void prioritizeFiles(const QVector<DownloadPriority> &priorities) override;
void setRatioLimit(qreal limit) override;
void setSeedingTimeLimit(int limit) override;
void setUploadLimit(int limit) override;
void setDownloadLimit(int limit) override;
void setSuperSeeding(bool enable) override;
void flushCache() const override;
void addTrackers(const QVector<TrackerEntry> &trackers) override;
void replaceTrackers(const QVector<TrackerEntry> &trackers) override;
void addUrlSeeds(const QVector<QUrl> &urlSeeds) override;
void removeUrlSeeds(const QVector<QUrl> &urlSeeds) override;
bool connectPeer(const PeerAddress &peerAddress) override;
QString createMagnetURI() const override;
bool needSaveResumeData() const;
// Session interface
lt::torrent_handle nativeHandle() const;
void handleAlert(const lt::alert *a);
void handleStateUpdate(const lt::torrent_status &nativeStatus);
void handleTempPathChanged();
void handleCategorySavePathChanged();
void handleAppendExtensionToggled();
void saveResumeData();
void handleStorageMoved(const QString &newPath, const QString &errorMessage);
private:
typedef std::function<void ()> EventTrigger;
#if (LIBTORRENT_VERSION_NUM < 10200)
using LTFileIndex = int;
#else
using LTFileIndex = lt::file_index_t;
#endif
void updateStatus();
void updateStatus(const lt::torrent_status &nativeStatus);
void updateState();
void updateTorrentInfo();
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
void handleFileCompletedAlert(const lt::file_completed_alert *p);
void handleFileRenamedAlert(const lt::file_renamed_alert *p);
void handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p);
void handleMetadataReceivedAlert(const lt::metadata_received_alert *p);
void handlePerformanceAlert(const lt::performance_alert *p) const;
void handleSaveResumeDataAlert(const lt::save_resume_data_alert *p);
void handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p);
void handleTorrentCheckedAlert(const lt::torrent_checked_alert *p);
void handleTorrentFinishedAlert(const lt::torrent_finished_alert *p);
void handleTorrentPausedAlert(const lt::torrent_paused_alert *p);
void handleTorrentResumedAlert(const lt::torrent_resumed_alert *p);
void handleTrackerErrorAlert(const lt::tracker_error_alert *p);
void handleTrackerReplyAlert(const lt::tracker_reply_alert *p);
void handleTrackerWarningAlert(const lt::tracker_warning_alert *p);
void resume_impl(bool forced);
bool isMoveInProgress() const;
QString actualStorageLocation() const;
bool isAutoManaged() const;
void setAutoManaged(bool enable);
void adjustActualSavePath();
void adjustActualSavePath_impl();
void move_impl(QString path, MoveStorageMode mode);
void moveStorage(const QString &newPath, MoveStorageMode mode);
void manageIncompleteFiles();
void setFirstLastPiecePriorityImpl(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
Session *const m_session;
lt::torrent_handle m_nativeHandle;
lt::torrent_status m_nativeStatus;
TorrentState m_state = TorrentState::Unknown;
TorrentInfo m_torrentInfo;
SpeedMonitor m_speedMonitor;
InfoHash m_hash;
// m_moveFinishedTriggers is activated only when the following conditions are met:
// all file rename jobs complete, all file move jobs complete
QQueue<EventTrigger> m_moveFinishedTriggers;
int m_renameCount = 0;
bool m_storageIsMoving = false;
// 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;
QHash<QString, TrackerInfo> m_trackerInfos;
// Persistent data
QString m_name;
QString m_savePath;
QString m_category;
QSet<QString> m_tags;
qreal m_ratioLimit;
int m_seedingTimeLimit;
bool m_hasSeedStatus;
bool m_tempPathDisabled;
bool m_fastresumeDataRejected = false;
bool m_hasMissingFiles = false;
bool m_hasRootFolder;
bool m_needsToSetFirstLastPiecePriority = false;
bool m_useAutoTMM;
bool m_unchecked = false;
};
}

View File

@@ -47,6 +47,7 @@
#include "base/exceptions.h" #include "base/exceptions.h"
#include "base/global.h" #include "base/global.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/io.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
#include "infohash.h" #include "infohash.h"
#include "trackerentry.h" #include "trackerentry.h"
@@ -166,12 +167,12 @@ void TorrentInfo::saveToFile(const QString &path) const
#endif #endif
const lt::entry torrentEntry = torrentCreator.generate(); const lt::entry torrentEntry = torrentCreator.generate();
QByteArray out; QFile torrentFile {path};
out.reserve(1024 * 1024); // most torrent file sizes are under 1 MB if (!torrentFile.open(QIODevice::WriteOnly))
lt::bencode(std::back_inserter(out), torrentEntry); throw RuntimeError {torrentFile.errorString()};
QFile torrentFile{path}; lt::bencode(Utils::IO::FileDeviceOutputIterator {torrentFile}, torrentEntry);
if (!torrentFile.open(QIODevice::WriteOnly) || (torrentFile.write(out) != out.size())) if (torrentFile.error() != QFileDevice::NoError)
throw RuntimeError {torrentFile.errorString()}; throw RuntimeError {torrentFile.errorString()};
} }

View File

@@ -70,7 +70,7 @@ namespace BitTorrent
// *Basic* Bittorrent tracker implementation // *Basic* Bittorrent tracker implementation
// [BEP-3] The BitTorrent Protocol Specification // [BEP-3] The BitTorrent Protocol Specification
// also see: https://wiki.theory.org/index.php/BitTorrentSpecification#Tracker_HTTP.2FHTTPS_Protocol // also see: https://wiki.theory.org/index.php/BitTorrentSpecification#Tracker_HTTP.2FHTTPS_Protocol
class Tracker : public QObject, public Http::IRequestHandler, private Http::ResponseBuilder class Tracker final : public QObject, public Http::IRequestHandler, private Http::ResponseBuilder
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(Tracker) Q_DISABLE_COPY(Tracker)

View File

@@ -41,7 +41,7 @@ namespace Http
class IRequestHandler; class IRequestHandler;
class Connection; class Connection;
class Server : public QTcpServer class Server final : public QTcpServer
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(Server) Q_DISABLE_COPY(Server)

View File

@@ -72,7 +72,7 @@ void Logger::freeInstance()
void Logger::addMessage(const QString &message, const Log::MsgType &type) void Logger::addMessage(const QString &message, const Log::MsgType &type)
{ {
QWriteLocker locker(&m_lock); QWriteLocker locker(&m_lock);
const Log::Msg msg = {m_msgCounter++, QDateTime::currentMSecsSinceEpoch(), type, message.toHtmlEscaped()}; const Log::Msg msg = {m_msgCounter++, type, QDateTime::currentMSecsSinceEpoch(), message};
m_messages.push_back(msg); m_messages.push_back(msg);
locker.unlock(); locker.unlock();
@@ -82,7 +82,7 @@ void Logger::addMessage(const QString &message, const Log::MsgType &type)
void Logger::addPeer(const QString &ip, const bool blocked, const QString &reason) void Logger::addPeer(const QString &ip, const bool blocked, const QString &reason)
{ {
QWriteLocker locker(&m_lock); QWriteLocker locker(&m_lock);
const Log::Peer msg = {m_peerCounter++, QDateTime::currentMSecsSinceEpoch(), ip.toHtmlEscaped(), blocked, reason.toHtmlEscaped()}; const Log::Peer msg = {m_peerCounter++, blocked, QDateTime::currentMSecsSinceEpoch(), ip, reason};
m_peers.push_back(msg); m_peers.push_back(msg);
locker.unlock(); locker.unlock();

View File

@@ -53,17 +53,17 @@ namespace Log
struct Msg struct Msg
{ {
int id; int id;
qint64 timestamp;
MsgType type; MsgType type;
qint64 timestamp;
QString message; QString message;
}; };
struct Peer struct Peer
{ {
int id; int id;
bool blocked;
qint64 timestamp; qint64 timestamp;
QString ip; QString ip;
bool blocked;
QString reason; QString reason;
}; };
} }

View File

@@ -52,7 +52,7 @@ namespace
// Disguise as Firefox to avoid web server banning // Disguise as Firefox to avoid web server banning
const char DEFAULT_USER_AGENT[] = "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"; const char DEFAULT_USER_AGENT[] = "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0";
class NetworkCookieJar : public QNetworkCookieJar class NetworkCookieJar final : public QNetworkCookieJar
{ {
public: public:
explicit NetworkCookieJar(QObject *parent = nullptr) explicit NetworkCookieJar(QObject *parent = nullptr)

View File

@@ -36,7 +36,7 @@
class QObject; class QObject;
class QUrl; class QUrl;
class DownloadHandlerImpl : public Net::DownloadHandler class DownloadHandlerImpl final : public Net::DownloadHandler
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(DownloadHandlerImpl) Q_DISABLE_COPY(DownloadHandlerImpl)

View File

@@ -743,6 +743,26 @@ void Preferences::setWebUiRootFolder(const QString &path)
setValue("Preferences/WebUI/RootFolder", path); setValue("Preferences/WebUI/RootFolder", path);
} }
bool Preferences::isWebUICustomHTTPHeadersEnabled() const
{
return value("Preferences/WebUI/CustomHTTPHeadersEnabled", false).toBool();
}
void Preferences::setWebUICustomHTTPHeadersEnabled(const bool enabled)
{
setValue("Preferences/WebUI/CustomHTTPHeadersEnabled", enabled);
}
QString Preferences::getWebUICustomHTTPHeaders() const
{
return value("Preferences/WebUI/CustomHTTPHeaders").toString();
}
void Preferences::setWebUICustomHTTPHeaders(const QString &headers)
{
setValue("Preferences/WebUI/CustomHTTPHeaders", headers);
}
bool Preferences::isDynDNSEnabled() const bool Preferences::isDynDNSEnabled() const
{ {
return value("Preferences/DynDNS/Enabled", false).toBool(); return value("Preferences/DynDNS/Enabled", false).toBool();

View File

@@ -223,6 +223,12 @@ public:
QString getWebUiRootFolder() const; QString getWebUiRootFolder() const;
void setWebUiRootFolder(const QString &path); void setWebUiRootFolder(const QString &path);
// WebUI custom HTTP headers
bool isWebUICustomHTTPHeadersEnabled() const;
void setWebUICustomHTTPHeadersEnabled(bool enabled);
QString getWebUICustomHTTPHeaders() const;
void setWebUICustomHTTPHeaders(const QString &headers);
// Dynamic DNS // Dynamic DNS
bool isDynDNSEnabled() const; bool isDynDNSEnabled() const;
void setDynDNSEnabled(bool enabled); void setDynDNSEnabled(bool enabled);

View File

@@ -63,7 +63,7 @@ namespace Private
}; };
/// Default implementation. Takes paths from system /// Default implementation. Takes paths from system
class DefaultProfile : public Profile class DefaultProfile final : public Profile
{ {
public: public:
explicit DefaultProfile(const QString &configurationName); explicit DefaultProfile(const QString &configurationName);
@@ -86,7 +86,7 @@ namespace Private
}; };
/// Custom tree: creates directories under the specified root directory /// Custom tree: creates directories under the specified root directory
class CustomProfile : public Profile class CustomProfile final : public Profile
{ {
public: public:
CustomProfile(const QString &rootPath, const QString &configurationName); CustomProfile(const QString &rootPath, const QString &configurationName);
@@ -114,14 +114,14 @@ namespace Private
virtual ~PathConverter() = default; virtual ~PathConverter() = default;
}; };
class NoConvertConverter : public PathConverter class NoConvertConverter final : public PathConverter
{ {
public: public:
QString toPortablePath(const QString &path) const override; QString toPortablePath(const QString &path) const override;
QString fromPortablePath(const QString &portablePath) const override; QString fromPortablePath(const QString &portablePath) const override;
}; };
class Converter : public PathConverter class Converter final : public PathConverter
{ {
public: public:
explicit Converter(const QString &basePath); explicit Converter(const QString &basePath);

View File

@@ -44,7 +44,7 @@
namespace namespace
{ {
class XmlStreamEntityResolver : public QXmlStreamEntityResolver class XmlStreamEntityResolver final : public QXmlStreamEntityResolver
{ {
public: public:
QString resolveUndeclaredEntity(const QString &name) override QString resolveUndeclaredEntity(const QString &name) override

View File

@@ -36,7 +36,7 @@ class QStringList;
class FileSystemWatcher; class FileSystemWatcher;
class ScanFoldersModel : public QAbstractListModel class ScanFoldersModel final : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(ScanFoldersModel) Q_DISABLE_COPY(ScanFoldersModel)

View File

@@ -28,6 +28,7 @@
#include "torrentfilter.h" #include "torrentfilter.h"
#include "bittorrent/infohash.h"
#include "bittorrent/torrenthandle.h" #include "bittorrent/torrenthandle.h"
const QString TorrentFilter::AnyCategory; const QString TorrentFilter::AnyCategory;

View File

@@ -36,58 +36,60 @@
// Because of the poor handling of UTF-8 characters in MSVC (emits warning C4819), // Because of the poor handling of UTF-8 characters in MSVC (emits warning C4819),
// we put all problematic UTF-8 chars/strings in this file. // we put all problematic UTF-8 chars/strings in this file.
// See issue #3059 for more details (https://github.com/qbittorrent/qBittorrent/issues/3059). // 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_COPYRIGHT[] = "©"; const char C_COPYRIGHT[] = "©";
const char C_INFINITY[] = "";
const char C_NON_BREAKING_SPACE[] = " ";
const char C_THIN_SPACE[] = ""; const char C_THIN_SPACE[] = "";
const char C_UTP[] = "μTP"; const char C_UTP[] = "μTP";
const char C_LOCALE_ARABIC[] = "عربي";
const char C_LOCALE_ARMENIAN[] = "Հայերեն";
const char C_LOCALE_BASQUE[] = "Euskara";
const char C_LOCALE_BULGARIAN[] = "Български";
const char C_LOCALE_BYELORUSSIAN[] = "Беларуская";
const char C_LOCALE_CATALAN[] = "Català";
const char C_LOCALE_CHINESE_SIMPLIFIED[] = "简体中文";
const char C_LOCALE_CHINESE_TRADITIONAL_HK[] = "香港正體字";
const char C_LOCALE_CHINESE_TRADITIONAL_TW[] = "正體中文";
const char C_LOCALE_CROATIAN[] = "Hrvatski";
const char C_LOCALE_CZECH[] = "Čeština";
const char C_LOCALE_DANISH[] = "Dansk";
const char C_LOCALE_DUTCH[] = "Nederlands";
const char C_LOCALE_ENGLISH[] = "English"; const char C_LOCALE_ENGLISH[] = "English";
const char C_LOCALE_ENGLISH_AUSTRALIA[] = "English(Australia)"; const char C_LOCALE_ENGLISH_AUSTRALIA[] = "English(Australia)";
const char C_LOCALE_ENGLISH_UNITEDKINGDOM[] = "English(United Kingdom)"; const char C_LOCALE_ENGLISH_UNITEDKINGDOM[] = "English(United Kingdom)";
const char C_LOCALE_ESPERANTO[] = "Esperanto"; const char C_LOCALE_ESPERANTO[] = "Esperanto";
const char C_LOCALE_FINNISH[] = "Suomi";
const char C_LOCALE_FRENCH[] = "Français"; const char C_LOCALE_FRENCH[] = "Français";
const char C_LOCALE_GALICIAN[] = "Galego";
const char C_LOCALE_GEORGIAN[] = "ქართული";
const char C_LOCALE_GERMAN[] = "Deutsch"; const char C_LOCALE_GERMAN[] = "Deutsch";
const char C_LOCALE_GREEK[] = "Ελληνικά";
const char C_LOCALE_HEBREW[] = "עברית";
const char C_LOCALE_HINDI[] = "हिन्दी, हिंदी";
const char C_LOCALE_HUNGARIAN[] = "Magyar"; const char C_LOCALE_HUNGARIAN[] = "Magyar";
const char C_LOCALE_ICELANDIC[] = "Íslenska"; const char C_LOCALE_ICELANDIC[] = "Íslenska";
const char C_LOCALE_INDONESIAN[] = "Bahasa Indonesia"; const char C_LOCALE_INDONESIAN[] = "Bahasa Indonesia";
const char C_LOCALE_ITALIAN[] = "Italiano"; const char C_LOCALE_ITALIAN[] = "Italiano";
const char C_LOCALE_DUTCH[] = "Nederlands"; const char C_LOCALE_JAPANESE[] = "日本語";
const char C_LOCALE_SPANISH[] = "Español"; const char C_LOCALE_KOREAN[] = "한글";
const char C_LOCALE_CATALAN[] = "Català";
const char C_LOCALE_GALICIAN[] = "Galego";
const char C_LOCALE_OCCITAN[] = "lenga d'òc";
const char C_LOCALE_PORTUGUESE[] = "Português";
const char C_LOCALE_PORTUGUESE_BRAZIL[] = "Português brasileiro";
const char C_LOCALE_POLISH[] = "Polski";
const char C_LOCALE_LATVIAN[] = "latviešu valoda"; const char C_LOCALE_LATVIAN[] = "latviešu valoda";
const char C_LOCALE_LITHUANIAN[] = "Lietuvių"; const char C_LOCALE_LITHUANIAN[] = "Lietuvių";
const char C_LOCALE_MALAY[] = "بهاس ملايو"; const char C_LOCALE_MALAY[] = "بهاس ملايو";
const char C_LOCALE_CZECH[] = "Čeština"; const char C_LOCALE_NORWEGIAN[] = "Norsk";
const char C_LOCALE_OCCITAN[] = "lenga d'òc";
const char C_LOCALE_POLISH[] = "Polski";
const char C_LOCALE_PORTUGUESE[] = "Português";
const char C_LOCALE_PORTUGUESE_BRAZIL[] = "Português brasileiro";
const char C_LOCALE_ROMANIAN[] = "Română";
const char C_LOCALE_RUSSIAN[] = "Русский";
const char C_LOCALE_SERBIAN[] = "Српски";
const char C_LOCALE_SLOVAK[] = "Slovenčina"; const char C_LOCALE_SLOVAK[] = "Slovenčina";
const char C_LOCALE_SLOVENIAN[] = "Slovenščina"; const char C_LOCALE_SLOVENIAN[] = "Slovenščina";
const char C_LOCALE_SERBIAN[] = "Српски"; const char C_LOCALE_SPANISH[] = "Español";
const char C_LOCALE_CROATIAN[] = "Hrvatski";
const char C_LOCALE_ARMENIAN[] = "Հայերեն";
const char C_LOCALE_ROMANIAN[] = "Română";
const char C_LOCALE_TURKISH[] = "Türkçe";
const char C_LOCALE_GREEK[] = "Ελληνικά";
const char C_LOCALE_SWEDISH[] = "Svenska"; const char C_LOCALE_SWEDISH[] = "Svenska";
const char C_LOCALE_FINNISH[] = "Suomi"; const char C_LOCALE_TURKISH[] = "Türkçe";
const char C_LOCALE_NORWEGIAN[] = "Norsk";
const char C_LOCALE_DANISH[] = "Dansk";
const char C_LOCALE_BULGARIAN[] = "Български";
const char C_LOCALE_UKRAINIAN[] = "Українська"; const char C_LOCALE_UKRAINIAN[] = "Українська";
const char C_LOCALE_UZBEK[] = "أۇزبېك‎"; const char C_LOCALE_UZBEK[] = "أۇزبېك‎";
const char C_LOCALE_RUSSIAN[] = "Русский";
const char C_LOCALE_JAPANESE[] = "日本語";
const char C_LOCALE_HEBREW[] = "עברית";
const char C_LOCALE_HINDI[] = "हिन्दी, हिंदी";
const char C_LOCALE_ARABIC[] = "عربي";
const char C_LOCALE_GEORGIAN[] = "ქართული";
const char C_LOCALE_BYELORUSSIAN[] = "Беларуская";
const char C_LOCALE_BASQUE[] = "Euskara";
const char C_LOCALE_VIETNAMESE[] = "tiếng Việt"; const char C_LOCALE_VIETNAMESE[] = "tiếng Việt";
const char C_LOCALE_CHINESE_TRADITIONAL_TW[] = "正體中文";
const char C_LOCALE_CHINESE_TRADITIONAL_HK[] = "香港正體字";
const char C_LOCALE_CHINESE_SIMPLIFIED[] = "简体中文";
const char C_LOCALE_KOREAN[] = "한글";

View File

@@ -190,9 +190,17 @@ namespace
path = getRegValue(hkInstallPath); path = getRegValue(hkInstallPath);
::RegCloseKey(hkInstallPath); ::RegCloseKey(hkInstallPath);
if (!path.isEmpty() && QDir(path).exists("python.exe")) { if (!path.isEmpty()) {
found = true; const QDir baseDir {path};
path = QDir(path).filePath("python.exe");
if (baseDir.exists("python3.exe")) {
found = true;
path = baseDir.filePath("python3.exe");
}
else if (baseDir.exists("python.exe")) {
found = true;
path = baseDir.filePath("python.exe");
}
} }
} }
} }
@@ -223,9 +231,13 @@ namespace
// Fallback: Detect python from default locations // Fallback: Detect python from default locations
const QFileInfoList dirs = QDir("C:/").entryInfoList({"Python*"}, QDir::Dirs, (QDir::Name | QDir::Reversed)); const QFileInfoList dirs = QDir("C:/").entryInfoList({"Python*"}, QDir::Dirs, (QDir::Name | QDir::Reversed));
for (const QFileInfo &info : dirs) { for (const QFileInfo &info : dirs) {
const QString path {info.absolutePath() + "/python.exe"}; const QString py3Path {info.absolutePath() + "/python3.exe"};
if (QFile::exists(path)) if (QFile::exists(py3Path))
return path; return py3Path;
const QString pyPath {info.absolutePath() + "/python.exe"};
if (QFile::exists(pyPath))
return pyPath;
} }
return {}; return {};
@@ -247,14 +259,9 @@ PythonInfo Utils::ForeignApps::pythonInfo()
{ {
static PythonInfo pyInfo; static PythonInfo pyInfo;
if (!pyInfo.isValid()) { if (!pyInfo.isValid()) {
#if defined(Q_OS_UNIX)
// On Unix-Like systems python3 should always exist
// https://www.python.org/dev/peps/pep-0394/
if (testPythonInstallation("python3", pyInfo)) if (testPythonInstallation("python3", pyInfo))
return pyInfo; return pyInfo;
#endif
// Look for "python" in Windows and in UNIX if "python3" is
// not detected.
if (testPythonInstallation("python", pyInfo)) if (testPythonInstallation("python", pyInfo))
return pyInfo; return pyInfo;

View File

@@ -28,6 +28,7 @@
#include "fs.h" #include "fs.h"
#include <cerrno>
#include <cstring> #include <cstring>
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)

75
src/base/utils/io.cpp Normal file
View File

@@ -0,0 +1,75 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Mike Tzou (Chocobo1)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "io.h"
#include <QByteArray>
#include <QFileDevice>
Utils::IO::FileDeviceOutputIterator::FileDeviceOutputIterator(QFileDevice &device, const int bufferSize)
: m_device {&device}
, m_buffer {std::make_shared<QByteArray>()}
, m_bufferSize {bufferSize}
{
m_buffer->reserve(bufferSize);
}
Utils::IO::FileDeviceOutputIterator::~FileDeviceOutputIterator()
{
if (m_buffer.use_count() == 1) {
if (m_device->error() == QFileDevice::NoError)
m_device->write(*m_buffer);
m_buffer->clear();
}
}
Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operator=(const char c)
{
m_buffer->append(c);
if (m_buffer->size() >= m_bufferSize) {
if (m_device->error() == QFileDevice::NoError)
m_device->write(*m_buffer);
m_buffer->clear();
}
return *this;
}
Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operator*()
{
return *this;
}
Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operator++()
{
return *this;
}
Utils::IO::FileDeviceOutputIterator &Utils::IO::FileDeviceOutputIterator::operator++(int)
{
return *this;
}

63
src/base/utils/io.h Normal file
View File

@@ -0,0 +1,63 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Mike Tzou (Chocobo1)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <iterator>
#include <memory>
class QByteArray;
class QFileDevice;
namespace Utils
{
namespace IO
{
// A wrapper class that satisfy LegacyOutputIterator requirement
class FileDeviceOutputIterator
: public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
explicit FileDeviceOutputIterator(QFileDevice &device, const int bufferSize = (4 * 1024));
FileDeviceOutputIterator(const FileDeviceOutputIterator &other) = default;
~FileDeviceOutputIterator();
// mimic std::ostream_iterator behavior
FileDeviceOutputIterator &operator=(char c);
// TODO: make these `constexpr` in C++17
FileDeviceOutputIterator &operator*();
FileDeviceOutputIterator &operator++();
FileDeviceOutputIterator &operator++(int);
private:
QFileDevice *m_device;
std::shared_ptr<QByteArray> m_buffer;
int m_bufferSize;
};
}
}

View File

@@ -28,7 +28,9 @@ fspathedit.h
hidabletabwidget.h hidabletabwidget.h
ipsubnetwhitelistoptionsdialog.h ipsubnetwhitelistoptionsdialog.h
lineedit.h lineedit.h
loglistwidget.h log/logfiltermodel.h
log/loglistview.h
log/logmodel.h
mainwindow.h mainwindow.h
optionsdialog.h optionsdialog.h
previewlistdelegate.h previewlistdelegate.h
@@ -81,7 +83,9 @@ fspathedit.cpp
hidabletabwidget.cpp hidabletabwidget.cpp
ipsubnetwhitelistoptionsdialog.cpp ipsubnetwhitelistoptionsdialog.cpp
lineedit.cpp lineedit.cpp
loglistwidget.cpp log/logfiltermodel.cpp
log/loglistview.cpp
log/logmodel.cpp
mainwindow.cpp mainwindow.cpp
optionsdialog.cpp optionsdialog.cpp
previewlistdelegate.cpp previewlistdelegate.cpp

View File

@@ -57,7 +57,7 @@ class PropListDelegate;
class TorrentContentFilterModel; class TorrentContentFilterModel;
class TorrentFileGuard; class TorrentFileGuard;
class AddNewTorrentDialog : public QDialog class AddNewTorrentDialog final : public QDialog
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(AddNewTorrentDialog) Q_DISABLE_COPY(AddNewTorrentDialog)

View File

@@ -114,7 +114,6 @@ enum AdvSettingsRows
// seeding // seeding
CHOKING_ALGORITHM, CHOKING_ALGORITHM,
SEED_CHOKING_ALGORITHM, SEED_CHOKING_ALGORITHM,
SUPER_SEEDING,
// tracker // tracker
ANNOUNCE_ALL_TRACKERS, ANNOUNCE_ALL_TRACKERS,
ANNOUNCE_ALL_TIERS, ANNOUNCE_ALL_TIERS,
@@ -223,8 +222,6 @@ void AdvancedSettings::saveAdvancedSettings()
// Peer resolution // Peer resolution
pref->resolvePeerCountries(m_checkBoxResolveCountries.isChecked()); pref->resolvePeerCountries(m_checkBoxResolveCountries.isChecked());
pref->resolvePeerHostNames(m_checkBoxResolveHosts.isChecked()); pref->resolvePeerHostNames(m_checkBoxResolveHosts.isChecked());
// Super seeding
session->setSuperSeedingEnabled(m_checkBoxSuperSeeding.isChecked());
// Network interface // Network interface
if (m_comboBoxInterface.currentIndex() == 0) { if (m_comboBoxInterface.currentIndex() == 0) {
// All interfaces (default) // All interfaces (default)
@@ -516,10 +513,6 @@ void AdvancedSettings::loadAdvancedSettings()
// Resolve peer hosts // Resolve peer hosts
m_checkBoxResolveHosts.setChecked(pref->resolvePeerHostNames()); m_checkBoxResolveHosts.setChecked(pref->resolvePeerHostNames());
addRow(RESOLVE_HOSTS, tr("Resolve peer host names"), &m_checkBoxResolveHosts); addRow(RESOLVE_HOSTS, tr("Resolve peer host names"), &m_checkBoxResolveHosts);
// Super seeding
m_checkBoxSuperSeeding.setChecked(session->isSuperSeedingEnabled());
addRow(SUPER_SEEDING, (tr("Strict super seeding") + ' ' + makeLink("https://www.libtorrent.org/reference-Settings.html#strict_super_seeding", "(?)"))
, &m_checkBoxSuperSeeding);
// Network interface // Network interface
m_comboBoxInterface.addItem(tr("Any interface", "i.e. Any network interface")); m_comboBoxInterface.addItem(tr("Any interface", "i.e. Any network interface"));
const QString currentInterface = session->networkInterface(); const QString currentInterface = session->networkInterface();

View File

@@ -61,7 +61,7 @@ private:
m_spinBoxSaveResumeDataInterval, m_spinBoxOutgoingPortsMin, m_spinBoxOutgoingPortsMax, m_spinBoxUPnPLeaseDuration, m_spinBoxSaveResumeDataInterval, m_spinBoxOutgoingPortsMin, m_spinBoxOutgoingPortsMax, m_spinBoxUPnPLeaseDuration,
m_spinBoxListRefresh, m_spinBoxTrackerPort, m_spinBoxCacheTTL, m_spinBoxSendBufferWatermark, m_spinBoxSendBufferLowWatermark, m_spinBoxListRefresh, m_spinBoxTrackerPort, m_spinBoxCacheTTL, m_spinBoxSendBufferWatermark, m_spinBoxSendBufferLowWatermark,
m_spinBoxSendBufferWatermarkFactor, m_spinBoxSocketBacklogSize, m_spinBoxStopTrackerTimeout, m_spinBoxSavePathHistoryLength; m_spinBoxSendBufferWatermarkFactor, m_spinBoxSocketBacklogSize, m_spinBoxStopTrackerTimeout, m_spinBoxSavePathHistoryLength;
QCheckBox m_checkBoxOsCache, m_checkBoxRecheckCompleted, m_checkBoxResolveCountries, m_checkBoxResolveHosts, m_checkBoxSuperSeeding, QCheckBox m_checkBoxOsCache, m_checkBoxRecheckCompleted, m_checkBoxResolveCountries, m_checkBoxResolveHosts,
m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus, m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus,
m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers, m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers,
m_checkBoxMultiConnectionsPerIp, m_checkBoxPieceExtentAffinity, m_checkBoxSuggestMode, m_checkBoxCoalesceRW, m_checkBoxSpeedWidgetEnabled; m_checkBoxMultiConnectionsPerIp, m_checkBoxPieceExtentAffinity, m_checkBoxSuggestMode, m_checkBoxCoalesceRW, m_checkBoxSpeedWidgetEnabled;

View File

@@ -39,7 +39,7 @@ namespace Ui
class AutoExpandableDialog; class AutoExpandableDialog;
} }
class AutoExpandableDialog : public QDialog class AutoExpandableDialog final : public QDialog
{ {
Q_OBJECT Q_OBJECT

View File

@@ -40,7 +40,7 @@ namespace BitTorrent
class TorrentHandle; class TorrentHandle;
} }
class CategoryFilterModel : public QAbstractItemModel class CategoryFilterModel final : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT

View File

@@ -33,7 +33,7 @@
class QString; class QString;
class CategoryFilterProxyModel : public QSortFilterProxyModel class CategoryFilterProxyModel final : public QSortFilterProxyModel
{ {
public: public:
explicit CategoryFilterProxyModel(QObject *parent = nullptr); explicit CategoryFilterProxyModel(QObject *parent = nullptr);

View File

@@ -30,7 +30,7 @@
#include <QTreeView> #include <QTreeView>
class CategoryFilterWidget : public QTreeView class CategoryFilterWidget final : public QTreeView
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(CategoryFilterWidget) Q_DISABLE_COPY(CategoryFilterWidget)

View File

@@ -38,7 +38,7 @@ namespace Ui
class CookiesDialog; class CookiesDialog;
} }
class CookiesDialog : public QDialog class CookiesDialog final : public QDialog
{ {
Q_OBJECT Q_OBJECT

View File

@@ -33,7 +33,7 @@
#include <QList> #include <QList>
#include <QNetworkCookie> #include <QNetworkCookie>
class CookiesModel : public QAbstractItemModel class CookiesModel final : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT

View File

@@ -29,35 +29,48 @@
#include "executionlogwidget.h" #include "executionlogwidget.h"
#include <QDateTime> #include <QDateTime>
#include <QMenu>
#include <QPalette> #include <QPalette>
#include "base/global.h" #include "log/logfiltermodel.h"
#include "loglistwidget.h" #include "log/loglistview.h"
#include "log/logmodel.h"
#include "ui_executionlogwidget.h" #include "ui_executionlogwidget.h"
#include "uithememanager.h" #include "uithememanager.h"
ExecutionLogWidget::ExecutionLogWidget(QWidget *parent, const Log::MsgTypes &types) ExecutionLogWidget::ExecutionLogWidget(const Log::MsgTypes types, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, m_ui(new Ui::ExecutionLogWidget) , m_ui(new Ui::ExecutionLogWidget)
, m_msgList(new LogListWidget(MAX_LOG_MESSAGES, types, this)) , m_messageFilterModel(new LogFilterModel(types, this))
, m_peerList(new LogListWidget(MAX_LOG_MESSAGES, Log::ALL, this))
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
LogMessageModel *messageModel = new LogMessageModel(this);
m_messageFilterModel->setSourceModel(messageModel);
LogListView *messageView = new LogListView(this);
messageView->setModel(m_messageFilterModel);
messageView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(messageView, &LogListView::customContextMenuRequested, this, [this, messageView, messageModel](const QPoint &pos)
{
displayContextMenu(pos, messageView, messageModel);
});
LogPeerModel *peerModel = new LogPeerModel(this);
LogListView *peerView = new LogListView(this);
peerView->setModel(peerModel);
peerView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(peerView, &LogListView::customContextMenuRequested, this, [this, peerView, peerModel](const QPoint &pos)
{
displayContextMenu(pos, peerView, peerModel);
});
m_ui->tabGeneral->layout()->addWidget(messageView);
m_ui->tabBan->layout()->addWidget(peerView);
#ifndef Q_OS_MACOS #ifndef Q_OS_MACOS
m_ui->tabConsole->setTabIcon(0, UIThemeManager::instance()->getIcon("view-calendar-journal")); m_ui->tabConsole->setTabIcon(0, UIThemeManager::instance()->getIcon("view-calendar-journal"));
m_ui->tabConsole->setTabIcon(1, UIThemeManager::instance()->getIcon("view-filter")); m_ui->tabConsole->setTabIcon(1, UIThemeManager::instance()->getIcon("view-filter"));
#endif #endif
m_ui->tabGeneral->layout()->addWidget(m_msgList);
m_ui->tabBan->layout()->addWidget(m_peerList);
const Logger *const logger = Logger::instance();
for (const Log::Msg &msg : asConst(logger->getMessages()))
addLogMessage(msg);
for (const Log::Peer &peer : asConst(logger->getPeers()))
addPeerMessage(peer);
connect(logger, &Logger::newLogMessage, this, &ExecutionLogWidget::addLogMessage);
connect(logger, &Logger::newLogPeer, this, &ExecutionLogWidget::addPeerMessage);
} }
ExecutionLogWidget::~ExecutionLogWidget() ExecutionLogWidget::~ExecutionLogWidget()
@@ -65,42 +78,24 @@ ExecutionLogWidget::~ExecutionLogWidget()
delete m_ui; delete m_ui;
} }
void ExecutionLogWidget::showMsgTypes(const Log::MsgTypes &types) void ExecutionLogWidget::setMessageTypes(const Log::MsgTypes types)
{ {
m_msgList->showMsgTypes(types); m_messageFilterModel->setMessageTypes(types);
} }
void ExecutionLogWidget::addLogMessage(const Log::Msg &msg) void ExecutionLogWidget::displayContextMenu(const QPoint &pos, const LogListView *view, const BaseLogModel *model) const
{ {
QString colorName; QMenu *menu = new QMenu;
switch (msg.type) { menu->setAttribute(Qt::WA_DeleteOnClose);
case Log::INFO:
colorName = QLatin1String("blue"); // only show copy action if any of the row is selected
break; if (view->currentIndex().isValid()) {
case Log::WARNING: const QAction *copyAct = menu->addAction(UIThemeManager::instance()->getIcon("edit-copy"), tr("Copy"));
colorName = QLatin1String("orange"); connect(copyAct, &QAction::triggered, view, &LogListView::copySelection);
break;
case Log::CRITICAL:
colorName = QLatin1String("red");
break;
default:
colorName = QApplication::palette().color(QPalette::WindowText).name();
} }
const QDateTime time = QDateTime::fromMSecsSinceEpoch(msg.timestamp); const QAction *clearAct = menu->addAction(UIThemeManager::instance()->getIcon("edit-clear"), tr("Clear"));
const QString text = QString::fromLatin1("<font color='grey'>%1</font> - <font color='%2'>%3</font>") connect(clearAct, &QAction::triggered, model, &BaseLogModel::reset);
.arg(time.toString(Qt::SystemLocaleShortDate), colorName, msg.message);
m_msgList->appendLine(text, msg.type);
}
void ExecutionLogWidget::addPeerMessage(const Log::Peer &peer) menu->popup(view->mapToGlobal(pos));
{
const QDateTime time = QDateTime::fromMSecsSinceEpoch(peer.timestamp);
const QString msg = QString::fromLatin1("<font color='grey'>%1</font> - <font color='red'>%2</font>")
.arg(time.toString(Qt::SystemLocaleShortDate), peer.ip);
const QString text = peer.blocked
? tr("%1 was blocked %2", "0.0.0.0 was blocked due to reason").arg(msg, peer.reason)
: tr("%1 was banned", "0.0.0.0 was banned").arg(msg);
m_peerList->appendLine(text, Log::NORMAL);
} }

View File

@@ -30,33 +30,33 @@
#define EXECUTIONLOGWIDGET_H #define EXECUTIONLOGWIDGET_H
#include <QWidget> #include <QWidget>
#include "base/logger.h"
class LogListWidget; #include "base/logger.h"
namespace Ui namespace Ui
{ {
class ExecutionLogWidget; class ExecutionLogWidget;
} }
class BaseLogModel;
class LogFilterModel;
class LogListView;
class ExecutionLogWidget : public QWidget class ExecutionLogWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
ExecutionLogWidget(QWidget *parent, const Log::MsgTypes &types); ExecutionLogWidget(Log::MsgTypes types, QWidget *parent);
void showMsgTypes(const Log::MsgTypes &types);
~ExecutionLogWidget(); ~ExecutionLogWidget();
private slots: void setMessageTypes(Log::MsgTypes types);
void addLogMessage(const Log::Msg &msg);
void addPeerMessage(const Log::Peer &peer);
private: private:
Ui::ExecutionLogWidget *m_ui; void displayContextMenu(const QPoint &pos, const LogListView *view, const BaseLogModel *model) const;
LogListWidget *m_msgList; Ui::ExecutionLogWidget *m_ui;
LogListWidget *m_peerList; LogFilterModel *m_messageFilterModel;
}; };
#endif // EXECUTIONLOGWIDGET_H #endif // EXECUTIONLOGWIDGET_H

View File

@@ -108,7 +108,7 @@ private:
}; };
/// Widget which uses QLineEdit for path editing /// Widget which uses QLineEdit for path editing
class FileSystemPathLineEdit : public FileSystemPathEdit class FileSystemPathLineEdit final : public FileSystemPathEdit
{ {
using base = FileSystemPathEdit; using base = FileSystemPathEdit;
using WidgetType = Private::FileLineEdit; using WidgetType = Private::FileLineEdit;
@@ -124,7 +124,7 @@ private:
}; };
/// Widget which uses QComboBox for path editing /// Widget which uses QComboBox for path editing
class FileSystemPathComboEdit : public FileSystemPathEdit class FileSystemPathComboEdit final : public FileSystemPathEdit
{ {
using base = FileSystemPathEdit; using base = FileSystemPathEdit;
using WidgetType = Private::FileComboEdit; using WidgetType = Private::FileComboEdit;

View File

@@ -22,7 +22,9 @@ HEADERS += \
$$PWD/hidabletabwidget.h \ $$PWD/hidabletabwidget.h \
$$PWD/ipsubnetwhitelistoptionsdialog.h \ $$PWD/ipsubnetwhitelistoptionsdialog.h \
$$PWD/lineedit.h \ $$PWD/lineedit.h \
$$PWD/loglistwidget.h \ $$PWD/log/logfiltermodel.h \
$$PWD/log/loglistview.h \
$$PWD/log/logmodel.h \
$$PWD/mainwindow.h \ $$PWD/mainwindow.h \
$$PWD/optionsdialog.h \ $$PWD/optionsdialog.h \
$$PWD/previewlistdelegate.h \ $$PWD/previewlistdelegate.h \
@@ -86,7 +88,9 @@ SOURCES += \
$$PWD/hidabletabwidget.cpp \ $$PWD/hidabletabwidget.cpp \
$$PWD/ipsubnetwhitelistoptionsdialog.cpp \ $$PWD/ipsubnetwhitelistoptionsdialog.cpp \
$$PWD/lineedit.cpp \ $$PWD/lineedit.cpp \
$$PWD/loglistwidget.cpp \ $$PWD/log/logfiltermodel.cpp \
$$PWD/log/loglistview.cpp \
$$PWD/log/logmodel.cpp \
$$PWD/mainwindow.cpp \ $$PWD/mainwindow.cpp \
$$PWD/optionsdialog.cpp \ $$PWD/optionsdialog.cpp \
$$PWD/previewlistdelegate.cpp \ $$PWD/previewlistdelegate.cpp \

View File

@@ -35,7 +35,7 @@
class QPaintEvent; class QPaintEvent;
#endif #endif
class HidableTabWidget : public QTabWidget class HidableTabWidget final : public QTabWidget
{ {
public: public:
explicit HidableTabWidget(QWidget *parent = nullptr); explicit HidableTabWidget(QWidget *parent = nullptr);

View File

@@ -14,7 +14,7 @@
class QToolButton; class QToolButton;
class LineEdit : public QLineEdit class LineEdit final : public QLineEdit
{ {
Q_OBJECT Q_OBJECT

View File

@@ -0,0 +1,52 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "logfiltermodel.h"
#include "logmodel.h"
LogFilterModel::LogFilterModel(const Log::MsgTypes types, QObject *parent)
: QSortFilterProxyModel(parent)
, m_types(types)
{
}
void LogFilterModel::setMessageTypes(const Log::MsgTypes types)
{
m_types = types;
invalidateFilter();
}
bool LogFilterModel::filterAcceptsRow(const int sourceRow, const QModelIndex &sourceParent) const
{
const QAbstractItemModel *const sourceModel = this->sourceModel();
const QModelIndex index = sourceModel->index(sourceRow, 0, sourceParent);
const Log::MsgType type = static_cast<Log::MsgType>(sourceModel->data(index, BaseLogModel::TypeRole).toInt());
return m_types.testFlag(type);
}

View File

@@ -0,0 +1,48 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <QSortFilterProxyModel>
#include "base/logger.h"
class LogFilterModel final : public QSortFilterProxyModel
{
Q_OBJECT
Q_DISABLE_COPY(LogFilterModel)
public:
explicit LogFilterModel(Log::MsgTypes types = Log::ALL, QObject *parent = nullptr);
void setMessageTypes(Log::MsgTypes types);
private:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
Log::MsgTypes m_types;
};

140
src/gui/log/loglistview.cpp Normal file
View File

@@ -0,0 +1,140 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Prince Gupta <jagannatharjun11@gmail.com>
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "loglistview.h"
#include <QApplication>
#include <QClipboard>
#include <QFontMetrics>
#include <QKeyEvent>
#include <QPainter>
#include <QStyle>
#include <QStyledItemDelegate>
#include "logmodel.h"
#include "uithememanager.h"
namespace
{
const QString SEPARATOR = QStringLiteral(" - ");
int horizontalAdvance(const QFontMetrics &fontMetrics, const QString &text)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
return fontMetrics.horizontalAdvance(text);
#else
return fontMetrics.width(text);
#endif
}
QString logText(const QModelIndex &index)
{
return QString::fromLatin1("%1%2%3").arg(index.data(BaseLogModel::TimeRole).toString(), SEPARATOR
, index.data(BaseLogModel::MessageRole).toString());
}
class LogItemDelegate final : public QStyledItemDelegate
{
public:
using QStyledItemDelegate::QStyledItemDelegate;
private:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
painter->save();
QStyledItemDelegate::paint(painter, option, index); // paints background, focus rect and selection rect
const QStyle *style = option.widget ? option.widget->style() : QApplication::style();;
const QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &option, option.widget)
.adjusted(1, 0, 0, 0); // shift 1 to avoid text being too close to focus rect
QFont font = option.font;
if (option.font.pointSize() > 0)
font.setPointSize(option.font.pointSize()); // somehow this needs to be set directly otherwise painter will use default font
painter->setFont(font);
const QPen originalPen = painter->pen();
QPen coloredPen = originalPen;
coloredPen.setColor(Qt::darkGray);
painter->setPen(coloredPen);
const QString time = index.data(BaseLogModel::TimeRole).toString();
style->drawItemText(painter, textRect, option.displayAlignment, option.palette, (option.state & QStyle::State_Enabled), time);
painter->setPen(originalPen);
const QFontMetrics fontMetrics = painter->fontMetrics(); // option.fontMetrics adds extra padding to QFontMetrics::width
const int separatorCoordinateX = horizontalAdvance(fontMetrics, time);
style->drawItemText(painter, textRect.adjusted(separatorCoordinateX, 0, 0, 0), option.displayAlignment, option.palette
, (option.state & QStyle::State_Enabled), SEPARATOR);
coloredPen.setColor(index.data(BaseLogModel::ForegroundRole).value<QColor>());
painter->setPen(coloredPen);
const int messageCoordinateX = separatorCoordinateX + horizontalAdvance(fontMetrics, SEPARATOR);
style->drawItemText(painter, textRect.adjusted(messageCoordinateX, 0, 0, 0), option.displayAlignment, option.palette
, (option.state & QStyle::State_Enabled), index.data(BaseLogModel::MessageRole).toString());
painter->restore();
}
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
const QSize minimumFontPadding(4, 4);
const QSize fontSize = option.fontMetrics.size(0, logText(index)) + minimumFontPadding;
const QSize defaultSize = QStyledItemDelegate::sizeHint(option, index);
const QSize margins = (defaultSize - fontSize).expandedTo({0, 0});
return fontSize + margins;
}
};
}
LogListView::LogListView(QWidget *parent)
: QListView(parent)
{
setSelectionMode(QAbstractItemView::ExtendedSelection);
setItemDelegate(new LogItemDelegate(this));
#if defined(Q_OS_MAC)
setAttribute(Qt::WA_MacShowFocusRect, false);
#endif
}
void LogListView::keyPressEvent(QKeyEvent *event)
{
if (event->matches(QKeySequence::Copy))
copySelection();
else
QListView::keyPressEvent(event);
}
void LogListView::copySelection() const
{
QStringList list;
const QModelIndexList selectedIndexes = selectionModel()->selectedRows();
for (const QModelIndex &index : selectedIndexes)
list.append(logText(index));
QApplication::clipboard()->setText(list.join('\n'));
}

View File

@@ -1,6 +1,7 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org> * Copyright (C) 2020 Prince Gupta <jagannatharjun11@gmail.com>
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@@ -26,35 +27,21 @@
* exception statement from your version. * exception statement from your version.
*/ */
#ifndef LOGLISTWIDGET_H #pragma once
#define LOGLISTWIDGET_H
#include <QListWidget> #include <QListView>
#include "base/logger.h"
class QKeyEvent; class LogListView final : public QListView
class LogListWidget : public QListWidget
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(LogListView)
public: public:
// -1 is the portable way to have all the bits set explicit LogListView(QWidget *parent = nullptr);
explicit LogListWidget(int maxLines, const Log::MsgTypes &types = Log::ALL, QWidget *parent = nullptr);
void showMsgTypes(const Log::MsgTypes &types);
public slots: public slots:
void appendLine(const QString &line, const Log::MsgType &type); void copySelection() const;
protected slots:
void copySelection();
protected:
void keyPressEvent(QKeyEvent *event) override;
private: private:
const int m_maxLines; void keyPressEvent(QKeyEvent *event) override;
Log::MsgTypes m_types;
}; };
#endif // LOGLISTWIDGET_H

188
src/gui/log/logmodel.cpp Normal file
View File

@@ -0,0 +1,188 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Prince Gupta <jagannatharjun11@gmail.com>
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "logmodel.h"
#include <QApplication>
#include <QDateTime>
#include <QColor>
#include <QPalette>
#include "base/global.h"
namespace
{
const int MAX_VISIBLE_MESSAGES = 20000;
}
BaseLogModel::Message::Message(const QString &time, const QString &message, const QColor &foreground, const Log::MsgType type)
: m_time(time)
, m_message(message)
, m_foreground(foreground)
, m_type(type)
{
}
QVariant BaseLogModel::Message::time() const
{
return m_time;
}
QVariant BaseLogModel::Message::message() const
{
return m_message;
}
QVariant BaseLogModel::Message::foreground() const
{
return m_foreground;
}
QVariant BaseLogModel::Message::type() const
{
return m_type;
}
BaseLogModel::BaseLogModel(QObject *parent)
: QAbstractListModel(parent)
, m_messages(MAX_VISIBLE_MESSAGES)
{
}
int BaseLogModel::rowCount(const QModelIndex &) const
{
return m_messages.size();
}
int BaseLogModel::columnCount(const QModelIndex &) const
{
return 1;
}
QVariant BaseLogModel::data(const QModelIndex &index, const int role) const
{
if (!index.isValid())
return {};
const int messageIndex = index.row();
if ((messageIndex < 0) || (messageIndex >= static_cast<int>(m_messages.size())))
return {};
const Message &message = m_messages[messageIndex];
switch (role) {
case TimeRole:
return message.time();
case MessageRole:
return message.message();
case ForegroundRole:
return message.foreground();
case TypeRole:
return message.type();
default:
return {};
}
}
void BaseLogModel::addNewMessage(const BaseLogModel::Message &message)
{
// if row is inserted on filled up buffer, the size will not change
// but because of calling of beginInsertRows function we'll have ghost rows.
if (m_messages.size() == MAX_VISIBLE_MESSAGES) {
const int lastMessage = m_messages.size() - 1;
beginRemoveRows(QModelIndex(), lastMessage, lastMessage);
m_messages.pop_back();
endRemoveRows();
}
beginInsertRows(QModelIndex(), 0, 0);
m_messages.push_front(message);
endInsertRows();
}
void BaseLogModel::reset()
{
beginResetModel();
m_messages.clear();
endResetModel();
}
LogMessageModel::LogMessageModel(QObject *parent)
: BaseLogModel(parent)
{
for (const Log::Msg &msg : asConst(Logger::instance()->getMessages()))
handleNewMessage(msg);
connect(Logger::instance(), &Logger::newLogMessage, this, &LogMessageModel::handleNewMessage);
}
void LogMessageModel::handleNewMessage(const Log::Msg &message)
{
const QString time = QDateTime::fromMSecsSinceEpoch(message.timestamp).toString(Qt::SystemLocaleShortDate);
const QString messageText = message.message;
QColor foreground;
switch (message.type) {
// The RGB QColor constructor is used for performance
case Log::NORMAL:
foreground = QApplication::palette().color(QPalette::WindowText);
break;
case Log::INFO:
foreground = QColor(0, 0, 255); // blue
break;
case Log::WARNING:
foreground = QColor(255, 165, 0); // orange
break;
case Log::CRITICAL:
foreground = QColor(255, 0, 0); // red
break;
default:
Q_ASSERT(false);
break;
}
addNewMessage({time, messageText, foreground, message.type});
}
LogPeerModel::LogPeerModel(QObject *parent)
: BaseLogModel(parent)
{
for (const Log::Peer &peer : asConst(Logger::instance()->getPeers()))
handleNewMessage(peer);
connect(Logger::instance(), &Logger::newLogPeer, this, &LogPeerModel::handleNewMessage);
}
void LogPeerModel::handleNewMessage(const Log::Peer &peer)
{
const QString time = QDateTime::fromMSecsSinceEpoch(peer.timestamp).toString(Qt::SystemLocaleShortDate);
const QString message = peer.blocked
? tr("%1 was blocked due to %2", "0.0.0.0 was blocked due to reason").arg(peer.ip, peer.reason)
: tr("%1 was banned", "0.0.0.0 was banned").arg(peer.ip);
const QColor foreground = Qt::red;
addNewMessage({time, message, foreground, Log::NORMAL});
}

104
src/gui/log/logmodel.h Normal file
View File

@@ -0,0 +1,104 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2020 Prince Gupta <jagannatharjun11@gmail.com>
* Copyright (C) 2019 sledgehammer999 <hammered999@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <boost/circular_buffer.hpp>
#include <QAbstractListModel>
#include "base/logger.h"
class BaseLogModel : public QAbstractListModel
{
Q_DISABLE_COPY(BaseLogModel)
public:
enum MessageTypeRole
{
TimeRole = Qt::UserRole,
MessageRole,
ForegroundRole,
TypeRole
};
explicit BaseLogModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = {}) const override;
int columnCount(const QModelIndex &parent = {}) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void reset();
protected:
class Message
{
public:
Message(const QString &time, const QString &message, const QColor &foreground, Log::MsgType type);
QVariant time() const;
QVariant message() const;
QVariant foreground() const;
QVariant type() const;
private:
QVariant m_time;
QVariant m_message;
QVariant m_foreground;
QVariant m_type;
};
void addNewMessage(const Message &message);
private:
boost::circular_buffer_space_optimized<Message> m_messages;
};
class LogMessageModel : public BaseLogModel
{
Q_OBJECT
Q_DISABLE_COPY(LogMessageModel)
public:
explicit LogMessageModel(QObject *parent = nullptr);
private slots:
void handleNewMessage(const Log::Msg &message);
};
class LogPeerModel : public BaseLogModel
{
Q_OBJECT
Q_DISABLE_COPY(LogPeerModel)
public:
explicit LogPeerModel(QObject *parent = nullptr);
private slots:
void handleNewMessage(const Log::Peer &peer);
};

View File

@@ -1,108 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "loglistwidget.h"
#include <QAction>
#include <QApplication>
#include <QClipboard>
#include <QKeyEvent>
#include <QLabel>
#include <QListWidgetItem>
#include <QRegularExpression>
#include "base/global.h"
#include "uithememanager.h"
LogListWidget::LogListWidget(const int maxLines, const Log::MsgTypes &types, QWidget *parent)
: QListWidget(parent)
, m_maxLines(maxLines)
, m_types(types)
{
// Allow multiple selections
setSelectionMode(QAbstractItemView::ExtendedSelection);
// Context menu
auto *copyAct = new QAction(UIThemeManager::instance()->getIcon("edit-copy"), tr("Copy"), this);
auto *clearAct = new QAction(UIThemeManager::instance()->getIcon("edit-clear"), tr("Clear"), this);
connect(copyAct, &QAction::triggered, this, &LogListWidget::copySelection);
connect(clearAct, &QAction::triggered, this, &LogListWidget::clear);
addAction(copyAct);
addAction(clearAct);
setContextMenuPolicy(Qt::ActionsContextMenu);
}
void LogListWidget::showMsgTypes(const Log::MsgTypes &types)
{
m_types = types;
for (int i = 0; i < count(); ++i) {
QListWidgetItem *tempItem = item(i);
if (!tempItem) continue;
Log::MsgType itemType = static_cast<Log::MsgType>(tempItem->data(Qt::UserRole).toInt());
setRowHidden(i, !(m_types & itemType));
}
}
void LogListWidget::keyPressEvent(QKeyEvent *event)
{
if (event->matches(QKeySequence::Copy))
copySelection();
else
QListWidget::keyPressEvent(event);
}
void LogListWidget::appendLine(const QString &line, const Log::MsgType &type)
{
// We need to use QLabel here to support rich text
auto *lbl = new QLabel(line);
lbl->setTextFormat(Qt::RichText);
lbl->setContentsMargins(4, 2, 4, 2);
auto *item = new QListWidgetItem;
item->setSizeHint(lbl->sizeHint());
item->setData(Qt::UserRole, type);
insertItem(0, item);
setItemWidget(item, lbl);
setRowHidden(0, !(m_types & type));
const int nbLines = count();
// Limit log size
if (nbLines > m_maxLines)
delete takeItem(nbLines - 1);
}
void LogListWidget::copySelection()
{
const QRegularExpression htmlTag("<[^>]+>");
QStringList strings;
for (QListWidgetItem *it : asConst(selectedItems()))
strings << static_cast<QLabel*>(itemWidget(it))->text().remove(htmlTag);
QApplication::clipboard()->setText(strings.join('\n'));
}

View File

@@ -28,6 +28,8 @@
#include "mainwindow.h" #include "mainwindow.h"
#include <chrono>
#include <QCloseEvent> #include <QCloseEvent>
#include <QDebug> #include <QDebug>
#include <QDesktopServices> #include <QDesktopServices>
@@ -99,9 +101,6 @@
#include "programupdater.h" #include "programupdater.h"
#endif #endif
#define TIME_TRAY_BALLOON 5000
#define PREVENT_SUSPEND_INTERVAL 60000
namespace namespace
{ {
#define SETTINGS_KEY(name) "GUI/" name #define SETTINGS_KEY(name) "GUI/" name
@@ -119,6 +118,11 @@ namespace
// Misc // Misc
const QString KEY_DOWNLOAD_TRACKER_FAVICON = QStringLiteral(SETTINGS_KEY("DownloadTrackerFavicon")); const QString KEY_DOWNLOAD_TRACKER_FAVICON = QStringLiteral(SETTINGS_KEY("DownloadTrackerFavicon"));
const std::chrono::seconds PREVENT_SUSPEND_INTERVAL {60};
#if !defined(Q_OS_MACOS)
const int TIME_TRAY_BALLOON = 5000;
#endif
// just a shortcut // just a shortcut
inline SettingsStorage *settings() inline SettingsStorage *settings()
{ {
@@ -487,7 +491,7 @@ int MainWindow::executionLogMsgTypes() const
void MainWindow::setExecutionLogMsgTypes(const int value) void MainWindow::setExecutionLogMsgTypes(const int value)
{ {
m_executionLog->showMsgTypes(static_cast<Log::MsgTypes>(value)); m_executionLog->setMessageTypes(static_cast<Log::MsgTypes>(value));
settings()->storeValue(KEY_EXECUTIONLOG_TYPES, value); settings()->storeValue(KEY_EXECUTIONLOG_TYPES, value);
} }
@@ -776,8 +780,9 @@ void MainWindow::cleanup()
if (m_systrayCreator) if (m_systrayCreator)
m_systrayCreator->stop(); m_systrayCreator->stop();
#endif #endif
if (m_preventTimer)
m_preventTimer->stop(); m_preventTimer->stop();
#if (defined(Q_OS_WIN) || defined(Q_OS_MACOS)) #if (defined(Q_OS_WIN) || defined(Q_OS_MACOS))
m_programUpdateTimer->stop(); m_programUpdateTimer->stop();
#endif #endif
@@ -1401,7 +1406,7 @@ void MainWindow::showStatusBar(bool show)
} }
} }
void MainWindow::loadPreferences(bool configureSession) void MainWindow::loadPreferences(const bool configureSession)
{ {
Logger::instance()->addMessage(tr("Options were saved successfully.")); Logger::instance()->addMessage(tr("Options were saved successfully."));
const Preferences *const pref = Preferences::instance(); const Preferences *const pref = Preferences::instance();
@@ -1451,8 +1456,11 @@ void MainWindow::loadPreferences(bool configureSession)
showStatusBar(pref->isStatusbarDisplayed()); showStatusBar(pref->isStatusbarDisplayed());
if ((pref->preventFromSuspendWhenDownloading() || pref->preventFromSuspendWhenSeeding()) && !m_preventTimer->isActive()) { if (pref->preventFromSuspendWhenDownloading() || pref->preventFromSuspendWhenSeeding()) {
m_preventTimer->start(PREVENT_SUSPEND_INTERVAL); if (!m_preventTimer->isActive()) {
updatePowerManagementState();
m_preventTimer->start(PREVENT_SUSPEND_INTERVAL);
}
} }
else { else {
m_preventTimer->stop(); m_preventTimer->stop();
@@ -1866,7 +1874,7 @@ void MainWindow::on_actionExecutionLogs_triggered(bool checked)
{ {
if (checked) { if (checked) {
Q_ASSERT(!m_executionLog); Q_ASSERT(!m_executionLog);
m_executionLog = new ExecutionLogWidget(m_tabs, static_cast<Log::MsgType>(executionLogMsgTypes())); m_executionLog = new ExecutionLogWidget(static_cast<Log::MsgType>(executionLogMsgTypes()), m_tabs);
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
m_tabs->addTab(m_executionLog, tr("Execution Log")); m_tabs->addTab(m_executionLog, tr("Execution Log"));
#else #else

View File

@@ -72,7 +72,7 @@ namespace Ui
class MainWindow; class MainWindow;
} }
class MainWindow : public QMainWindow class MainWindow final : public QMainWindow
{ {
Q_OBJECT Q_OBJECT

View File

@@ -81,9 +81,76 @@ namespace
ret.append(locale.toString(date.addDays(i), "dddd")); ret.append(locale.toString(date.addDays(i), "dddd"));
return ret; return ret;
} }
QString languageToLocalizedString(const QLocale &locale)
{
switch (locale.language()) {
case QLocale::Arabic: return QString::fromUtf8(C_LOCALE_ARABIC);
case QLocale::Armenian: return QString::fromUtf8(C_LOCALE_ARMENIAN);
case QLocale::Basque: return QString::fromUtf8(C_LOCALE_BASQUE);
case QLocale::Bulgarian: return QString::fromUtf8(C_LOCALE_BULGARIAN);
case QLocale::Byelorussian: return QString::fromUtf8(C_LOCALE_BYELORUSSIAN);
case QLocale::Catalan: return QString::fromUtf8(C_LOCALE_CATALAN);
case QLocale::Chinese:
switch (locale.country()) {
case QLocale::China: return QString::fromUtf8(C_LOCALE_CHINESE_SIMPLIFIED);
case QLocale::HongKong: return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_HK);
default: return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_TW);
}
case QLocale::Croatian: return QString::fromUtf8(C_LOCALE_CROATIAN);
case QLocale::Czech: return QString::fromUtf8(C_LOCALE_CZECH);
case QLocale::Danish: return QString::fromUtf8(C_LOCALE_DANISH);
case QLocale::Dutch: return QString::fromUtf8(C_LOCALE_DUTCH);
case QLocale::English:
switch (locale.country()) {
case QLocale::Australia: return QString::fromUtf8(C_LOCALE_ENGLISH_AUSTRALIA);
case QLocale::UnitedKingdom: return QString::fromUtf8(C_LOCALE_ENGLISH_UNITEDKINGDOM);
default: return QString::fromUtf8(C_LOCALE_ENGLISH);
}
case QLocale::Finnish: return QString::fromUtf8(C_LOCALE_FINNISH);
case QLocale::French: return QString::fromUtf8(C_LOCALE_FRENCH);
case QLocale::Galician: return QString::fromUtf8(C_LOCALE_GALICIAN);
case QLocale::Georgian: return QString::fromUtf8(C_LOCALE_GEORGIAN);
case QLocale::German: return QString::fromUtf8(C_LOCALE_GERMAN);
case QLocale::Greek: return QString::fromUtf8(C_LOCALE_GREEK);
case QLocale::Hebrew: return QString::fromUtf8(C_LOCALE_HEBREW);
case QLocale::Hindi: return QString::fromUtf8(C_LOCALE_HINDI);
case QLocale::Hungarian: return QString::fromUtf8(C_LOCALE_HUNGARIAN);
case QLocale::Icelandic: return QString::fromUtf8(C_LOCALE_ICELANDIC);
case QLocale::Indonesian: return QString::fromUtf8(C_LOCALE_INDONESIAN);
case QLocale::Italian: return QString::fromUtf8(C_LOCALE_ITALIAN);
case QLocale::Japanese: return QString::fromUtf8(C_LOCALE_JAPANESE);
case QLocale::Korean: return QString::fromUtf8(C_LOCALE_KOREAN);
case QLocale::Latvian: return QString::fromUtf8(C_LOCALE_LATVIAN);
case QLocale::Lithuanian: return QString::fromUtf8(C_LOCALE_LITHUANIAN);
case QLocale::Malay: return QString::fromUtf8(C_LOCALE_MALAY);
case QLocale::Norwegian: return QString::fromUtf8(C_LOCALE_NORWEGIAN);
case QLocale::Occitan: return QString::fromUtf8(C_LOCALE_OCCITAN);
case QLocale::Polish: return QString::fromUtf8(C_LOCALE_POLISH);
case QLocale::Portuguese:
if (locale.country() == QLocale::Brazil)
return QString::fromUtf8(C_LOCALE_PORTUGUESE_BRAZIL);
return QString::fromUtf8(C_LOCALE_PORTUGUESE);
case QLocale::Romanian: return QString::fromUtf8(C_LOCALE_ROMANIAN);
case QLocale::Russian: return QString::fromUtf8(C_LOCALE_RUSSIAN);
case QLocale::Serbian: return QString::fromUtf8(C_LOCALE_SERBIAN);
case QLocale::Slovak: return QString::fromUtf8(C_LOCALE_SLOVAK);
case QLocale::Slovenian: return QString::fromUtf8(C_LOCALE_SLOVENIAN);
case QLocale::Spanish: return QString::fromUtf8(C_LOCALE_SPANISH);
case QLocale::Swedish: return QString::fromUtf8(C_LOCALE_SWEDISH);
case QLocale::Turkish: return QString::fromUtf8(C_LOCALE_TURKISH);
case QLocale::Ukrainian: return QString::fromUtf8(C_LOCALE_UKRAINIAN);
case QLocale::Uzbek: return QString::fromUtf8(C_LOCALE_UZBEK);
case QLocale::Vietnamese: return QString::fromUtf8(C_LOCALE_VIETNAMESE);
default:
const QString lang = QLocale::languageToString(locale.language());
qWarning() << "Unrecognized language name: " << lang;
return lang;
}
}
} }
class WheelEventEater : public QObject class WheelEventEater final : public QObject
{ {
public: public:
using QObject::QObject; using QObject::QObject;
@@ -436,6 +503,8 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(m_ui->DNSPasswordTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->DNSPasswordTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
connect(m_ui->groupAltWebUI, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->groupAltWebUI, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->textWebUIRootFolder, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->textWebUIRootFolder, &FileSystemPathLineEdit::selectedPathChanged, this, &ThisType::enableApplyButton);
connect(m_ui->groupWebUIAddCustomHTTPHeaders, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->textWebUICustomHTTPHeaders, &QPlainTextEdit::textChanged, this, &OptionsDialog::enableApplyButton);
#endif // DISABLE_WEBUI #endif // DISABLE_WEBUI
// RSS tab // RSS tab
@@ -795,6 +864,9 @@ void OptionsDialog::saveOptions()
// Alternative UI // Alternative UI
pref->setAltWebUiEnabled(m_ui->groupAltWebUI->isChecked()); pref->setAltWebUiEnabled(m_ui->groupAltWebUI->isChecked());
pref->setWebUiRootFolder(m_ui->textWebUIRootFolder->selectedPath()); pref->setWebUiRootFolder(m_ui->textWebUIRootFolder->selectedPath());
// Custom HTTP headers
pref->setWebUICustomHTTPHeadersEnabled(m_ui->groupWebUIAddCustomHTTPHeaders->isChecked());
pref->setWebUICustomHTTPHeaders(m_ui->textWebUICustomHTTPHeaders->toPlainText());
} }
// End Web UI // End Web UI
// End preferences // End preferences
@@ -815,7 +887,6 @@ Net::ProxyType OptionsDialog::getProxyType() const
switch (m_ui->comboProxyType->currentIndex()) { switch (m_ui->comboProxyType->currentIndex()) {
case 1: case 1:
return Net::ProxyType::SOCKS4; return Net::ProxyType::SOCKS4;
break;
case 2: case 2:
if (isProxyAuthEnabled()) if (isProxyAuthEnabled())
return Net::ProxyType::SOCKS5_PW; return Net::ProxyType::SOCKS5_PW;
@@ -1176,6 +1247,9 @@ void OptionsDialog::loadOptions()
m_ui->groupAltWebUI->setChecked(pref->isAltWebUiEnabled()); m_ui->groupAltWebUI->setChecked(pref->isAltWebUiEnabled());
m_ui->textWebUIRootFolder->setSelectedPath(pref->getWebUiRootFolder()); m_ui->textWebUIRootFolder->setSelectedPath(pref->getWebUiRootFolder());
// Custom HTTP headers
m_ui->groupWebUIAddCustomHTTPHeaders->setChecked(pref->isWebUICustomHTTPHeadersEnabled());
m_ui->textWebUICustomHTTPHeaders->setPlainText(pref->getWebUICustomHTTPHeaders());
// End Web UI preferences // End Web UI preferences
} }
@@ -1376,28 +1450,27 @@ void OptionsDialog::toggleComboRatioLimitAct()
m_ui->comboRatioLimitAct->setEnabled(m_ui->checkMaxRatio->isChecked() || m_ui->checkMaxSeedingMinutes->isChecked()); m_ui->comboRatioLimitAct->setEnabled(m_ui->checkMaxRatio->isChecked() || m_ui->checkMaxSeedingMinutes->isChecked());
} }
void OptionsDialog::enableProxy(int index) void OptionsDialog::enableProxy(const int index)
{ {
if (index) { if (index >= 1) { // Any proxy type is used
//enable //enable
m_ui->lblProxyIP->setEnabled(true); m_ui->lblProxyIP->setEnabled(true);
m_ui->textProxyIP->setEnabled(true); m_ui->textProxyIP->setEnabled(true);
m_ui->lblProxyPort->setEnabled(true); m_ui->lblProxyPort->setEnabled(true);
m_ui->spinProxyPort->setEnabled(true); m_ui->spinProxyPort->setEnabled(true);
m_ui->checkProxyPeerConnecs->setEnabled(true); m_ui->checkProxyPeerConnecs->setEnabled(true);
if (index > 1) { if (index >= 2) { // SOCKS5 or HTTP
m_ui->checkProxyAuth->setEnabled(true); m_ui->checkProxyAuth->setEnabled(true);
m_ui->isProxyOnlyForTorrents->setEnabled(true); m_ui->isProxyOnlyForTorrents->setEnabled(true);
} }
else { else {
m_ui->checkProxyAuth->setEnabled(false); m_ui->checkProxyAuth->setEnabled(false);
m_ui->checkProxyAuth->setChecked(false);
m_ui->isProxyOnlyForTorrents->setEnabled(false); m_ui->isProxyOnlyForTorrents->setEnabled(false);
m_ui->isProxyOnlyForTorrents->setChecked(true); m_ui->isProxyOnlyForTorrents->setChecked(true);
} }
} }
else { else { // No proxy
//disable // disable
m_ui->lblProxyIP->setEnabled(false); m_ui->lblProxyIP->setEnabled(false);
m_ui->textProxyIP->setEnabled(false); m_ui->textProxyIP->setEnabled(false);
m_ui->lblProxyPort->setEnabled(false); m_ui->lblProxyPort->setEnabled(false);
@@ -1405,7 +1478,6 @@ void OptionsDialog::enableProxy(int index)
m_ui->checkProxyPeerConnecs->setEnabled(false); m_ui->checkProxyPeerConnecs->setEnabled(false);
m_ui->isProxyOnlyForTorrents->setEnabled(false); m_ui->isProxyOnlyForTorrents->setEnabled(false);
m_ui->checkProxyAuth->setEnabled(false); m_ui->checkProxyAuth->setEnabled(false);
m_ui->checkProxyAuth->setChecked(false);
} }
} }
@@ -1698,81 +1770,6 @@ void OptionsDialog::handleIPFilterParsed(bool error, int ruleCount)
disconnect(BitTorrent::Session::instance(), &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed); disconnect(BitTorrent::Session::instance(), &BitTorrent::Session::IPFilterParsed, this, &OptionsDialog::handleIPFilterParsed);
} }
QString OptionsDialog::languageToLocalizedString(const QLocale &locale)
{
switch (locale.language()) {
case QLocale::English: {
if (locale.country() == QLocale::Australia)
return QString::fromUtf8(C_LOCALE_ENGLISH_AUSTRALIA);
if (locale.country() == QLocale::UnitedKingdom)
return QString::fromUtf8(C_LOCALE_ENGLISH_UNITEDKINGDOM);
return QString::fromUtf8(C_LOCALE_ENGLISH);
}
case QLocale::French: return QString::fromUtf8(C_LOCALE_FRENCH);
case QLocale::German: return QString::fromUtf8(C_LOCALE_GERMAN);
case QLocale::Hungarian: return QString::fromUtf8(C_LOCALE_HUNGARIAN);
case QLocale::Icelandic: return QString::fromUtf8(C_LOCALE_ICELANDIC);
case QLocale::Indonesian: return QString::fromUtf8(C_LOCALE_INDONESIAN);
case QLocale::Italian: return QString::fromUtf8(C_LOCALE_ITALIAN);
case QLocale::Dutch: return QString::fromUtf8(C_LOCALE_DUTCH);
case QLocale::Spanish: return QString::fromUtf8(C_LOCALE_SPANISH);
case QLocale::Catalan: return QString::fromUtf8(C_LOCALE_CATALAN);
case QLocale::Galician: return QString::fromUtf8(C_LOCALE_GALICIAN);
case QLocale::Occitan: return QString::fromUtf8(C_LOCALE_OCCITAN);
case QLocale::Portuguese: {
if (locale.country() == QLocale::Brazil)
return QString::fromUtf8(C_LOCALE_PORTUGUESE_BRAZIL);
return QString::fromUtf8(C_LOCALE_PORTUGUESE);
}
case QLocale::Polish: return QString::fromUtf8(C_LOCALE_POLISH);
case QLocale::Latvian: return QString::fromUtf8(C_LOCALE_LATVIAN);
case QLocale::Lithuanian: return QString::fromUtf8(C_LOCALE_LITHUANIAN);
case QLocale::Malay: return QString::fromUtf8(C_LOCALE_MALAY);
case QLocale::Czech: return QString::fromUtf8(C_LOCALE_CZECH);
case QLocale::Slovak: return QString::fromUtf8(C_LOCALE_SLOVAK);
case QLocale::Slovenian: return QString::fromUtf8(C_LOCALE_SLOVENIAN);
case QLocale::Serbian: return QString::fromUtf8(C_LOCALE_SERBIAN);
case QLocale::Croatian: return QString::fromUtf8(C_LOCALE_CROATIAN);
case QLocale::Armenian: return QString::fromUtf8(C_LOCALE_ARMENIAN);
case QLocale::Romanian: return QString::fromUtf8(C_LOCALE_ROMANIAN);
case QLocale::Turkish: return QString::fromUtf8(C_LOCALE_TURKISH);
case QLocale::Greek: return QString::fromUtf8(C_LOCALE_GREEK);
case QLocale::Swedish: return QString::fromUtf8(C_LOCALE_SWEDISH);
case QLocale::Finnish: return QString::fromUtf8(C_LOCALE_FINNISH);
case QLocale::Norwegian: return QString::fromUtf8(C_LOCALE_NORWEGIAN);
case QLocale::Danish: return QString::fromUtf8(C_LOCALE_DANISH);
case QLocale::Bulgarian: return QString::fromUtf8(C_LOCALE_BULGARIAN);
case QLocale::Ukrainian: return QString::fromUtf8(C_LOCALE_UKRAINIAN);
case QLocale::Uzbek: return QString::fromUtf8(C_LOCALE_UZBEK);
case QLocale::Russian: return QString::fromUtf8(C_LOCALE_RUSSIAN);
case QLocale::Japanese: return QString::fromUtf8(C_LOCALE_JAPANESE);
case QLocale::Hebrew: return QString::fromUtf8(C_LOCALE_HEBREW);
case QLocale::Hindi: return QString::fromUtf8(C_LOCALE_HINDI);
case QLocale::Arabic: return QString::fromUtf8(C_LOCALE_ARABIC);
case QLocale::Georgian: return QString::fromUtf8(C_LOCALE_GEORGIAN);
case QLocale::Byelorussian: return QString::fromUtf8(C_LOCALE_BYELORUSSIAN);
case QLocale::Basque: return QString::fromUtf8(C_LOCALE_BASQUE);
case QLocale::Vietnamese: return QString::fromUtf8(C_LOCALE_VIETNAMESE);
case QLocale::Chinese: {
switch (locale.country()) {
case QLocale::China:
return QString::fromUtf8(C_LOCALE_CHINESE_SIMPLIFIED);
case QLocale::HongKong:
return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_HK);
default:
return QString::fromUtf8(C_LOCALE_CHINESE_TRADITIONAL_TW);
}
}
case QLocale::Korean: return QString::fromUtf8(C_LOCALE_KOREAN);
default: {
// Fallback to English
const QString engLang = QLocale::languageToString(locale.language());
qWarning() << "Unrecognized language name: " << engLang;
return engLang;
}
}
}
bool OptionsDialog::schedTimesOk() bool OptionsDialog::schedTimesOk()
{ {
if (m_ui->timeEditScheduleFrom->time() == m_ui->timeEditScheduleTo->time()) { if (m_ui->timeEditScheduleFrom->time() == m_ui->timeEditScheduleTo->time()) {

View File

@@ -56,7 +56,7 @@ namespace Ui
class OptionsDialog; class OptionsDialog;
} }
class OptionsDialog : public QDialog class OptionsDialog final : public QDialog
{ {
Q_OBJECT Q_OBJECT
using ThisType = OptionsDialog; using ThisType = OptionsDialog;
@@ -82,7 +82,7 @@ class OptionsDialog : public QDialog
public: public:
// Constructor / Destructor // Constructor / Destructor
OptionsDialog(QWidget *parent = nullptr); OptionsDialog(QWidget *parent = nullptr);
~OptionsDialog(); ~OptionsDialog() override;
public slots: public slots:
void showConnectionTab(); void showConnectionTab();
@@ -117,7 +117,6 @@ private:
void saveOptions(); void saveOptions();
void loadOptions(); void loadOptions();
void initializeLanguageCombo(); void initializeLanguageCombo();
static QString languageToLocalizedString(const QLocale &locale);
// General options // General options
QString getLocale() const; QString getLocale() const;
#ifndef Q_OS_MACOS #ifndef Q_OS_MACOS

View File

@@ -446,7 +446,7 @@
<item> <item>
<widget class="QCheckBox" name="checkCloseToSystray"> <widget class="QCheckBox" name="checkCloseToSystray">
<property name="toolTip"> <property name="toolTip">
<string>The systray icon will still be visible when closing the main window</string> <string>The systray icon will still be visible when closing the main window</string>
</property> </property>
<property name="text"> <property name="text">
<string extracomment="The systray icon will still be visible when closing the main window">Close qBittorrent to notification area</string> <string extracomment="The systray icon will still be visible when closing the main window">Close qBittorrent to notification area</string>
@@ -471,12 +471,12 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>Monochrome (Dark theme)</string> <string>Monochrome (for dark theme)</string>
</property> </property>
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>Monochrome (Light theme)</string> <string>Monochrome (for light theme)</string>
</property> </property>
</item> </item>
</widget> </widget>
@@ -3220,6 +3220,28 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupWebUIAddCustomHTTPHeaders">
<property name="title">
<string>Add custom HTTP headers</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QPlainTextEdit" name="textWebUICustomHTTPHeaders">
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
<property name="placeholderText">
<string>Header: value pairs, one per line</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="checkDynDNS"> <widget class="QGroupBox" name="checkDynDNS">
<property name="title"> <property name="title">

View File

@@ -30,10 +30,6 @@
#include <QtGlobal> #include <QtGlobal>
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
#include "powermanagement_x11.h"
#endif
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
#include <IOKit/pwr_mgt/IOPMLib.h> #include <IOKit/pwr_mgt/IOPMLib.h>
#endif #endif
@@ -42,9 +38,12 @@
#include <windows.h> #include <windows.h>
#endif #endif
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
#include "powermanagement_x11.h"
#endif
PowerManagement::PowerManagement(QObject *parent) PowerManagement::PowerManagement(QObject *parent)
: QObject(parent) : QObject(parent)
, m_busy(false)
{ {
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB) #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
m_inhibitor = new PowerManagementInhibitor(this); m_inhibitor = new PowerManagementInhibitor(this);
@@ -55,7 +54,7 @@ PowerManagement::~PowerManagement()
{ {
} }
void PowerManagement::setActivityState(bool busy) void PowerManagement::setActivityState(const bool busy)
{ {
if (busy) if (busy)
setBusy(); setBusy();

View File

@@ -52,11 +52,11 @@ public:
void setActivityState(bool busy); void setActivityState(bool busy);
private: private:
bool m_busy;
void setBusy(); void setBusy();
void setIdle(); void setIdle();
bool m_busy = false;
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB) #if (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)) && defined(QT_DBUS_LIB)
PowerManagementInhibitor *m_inhibitor; PowerManagementInhibitor *m_inhibitor;
#endif #endif

View File

@@ -31,7 +31,7 @@
#include <QItemDelegate> #include <QItemDelegate>
class PreviewListDelegate : public QItemDelegate class PreviewListDelegate final : public QItemDelegate
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(PreviewListDelegate) Q_DISABLE_COPY(PreviewListDelegate)

View File

@@ -45,7 +45,7 @@ namespace Ui
} }
class PreviewListDelegate; class PreviewListDelegate;
class PreviewSelectDialog : public QDialog class PreviewSelectDialog final : public QDialog
{ {
Q_OBJECT Q_OBJECT

View File

@@ -45,7 +45,7 @@ class QStringRef;
namespace Private namespace Private
{ {
class FileSystemPathValidator : public QValidator class FileSystemPathValidator final : public QValidator
{ {
Q_OBJECT Q_OBJECT
@@ -111,7 +111,7 @@ namespace Private
virtual QWidget *widget() = 0; virtual QWidget *widget() = 0;
}; };
class FileLineEdit : public QLineEdit, public FileEditorWithCompletion class FileLineEdit final : public QLineEdit, public FileEditorWithCompletion
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(FileLineEdit) Q_DISABLE_COPY(FileLineEdit)
@@ -141,7 +141,7 @@ namespace Private
QAction *m_warningAction; QAction *m_warningAction;
}; };
class FileComboEdit : public QComboBox, public FileEditorWithCompletion class FileComboEdit final : public QComboBox, public FileEditorWithCompletion
{ {
Q_OBJECT Q_OBJECT

View File

@@ -32,7 +32,7 @@
class QString; class QString;
class TriStateWidget : public QWidget class TriStateWidget final : public QWidget
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(TriStateWidget) Q_DISABLE_COPY(TriStateWidget)

View File

@@ -36,7 +36,7 @@
class QWidget; class QWidget;
class DownloadedPiecesBar : public PiecesBar class DownloadedPiecesBar final : public PiecesBar
{ {
using base = PiecesBar; using base = PiecesBar;
Q_OBJECT Q_OBJECT

View File

@@ -31,7 +31,7 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
class PeerListSortModel : public QSortFilterProxyModel class PeerListSortModel final : public QSortFilterProxyModel
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(PeerListSortModel) Q_DISABLE_COPY(PeerListSortModel)

View File

@@ -53,7 +53,7 @@ namespace Net
class ReverseResolution; class ReverseResolution;
} }
class PeerListWidget : public QTreeView class PeerListWidget final : public QTreeView
{ {
Q_OBJECT Q_OBJECT

View File

@@ -31,7 +31,7 @@
#include "piecesbar.h" #include "piecesbar.h"
class PieceAvailabilityBar : public PiecesBar class PieceAvailabilityBar final : public PiecesBar
{ {
using base = PiecesBar; using base = PiecesBar;
Q_OBJECT Q_OBJECT

View File

@@ -37,7 +37,9 @@
#include <QTextStream> #include <QTextStream>
#include <QToolTip> #include <QToolTip>
#include "base/indexrange.h"
#include "base/bittorrent/torrenthandle.h" #include "base/bittorrent/torrenthandle.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
namespace namespace

View File

@@ -43,6 +43,7 @@
#include <QUrl> #include <QUrl>
#include "base/bittorrent/downloadpriority.h" #include "base/bittorrent/downloadpriority.h"
#include "base/bittorrent/infohash.h"
#include "base/bittorrent/session.h" #include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h" #include "base/bittorrent/torrenthandle.h"
#include "base/preferences.h" #include "base/preferences.h"

View File

@@ -49,7 +49,7 @@ enum PropColumn
AVAILABILITY AVAILABILITY
}; };
class PropListDelegate : public QItemDelegate class PropListDelegate final : public QItemDelegate
{ {
Q_OBJECT Q_OBJECT

View File

@@ -38,7 +38,7 @@
class QPen; class QPen;
class SpeedPlotView : public QGraphicsView class SpeedPlotView final : public QGraphicsView
{ {
Q_OBJECT Q_OBJECT

View File

@@ -40,7 +40,7 @@ class QVBoxLayout;
class PropertiesWidget; class PropertiesWidget;
class SpeedPlotView; class SpeedPlotView;
class ComboBoxMenuButton : public QComboBox class ComboBoxMenuButton final : public QComboBox
{ {
Q_OBJECT Q_OBJECT

View File

@@ -31,7 +31,7 @@
#include <QMessageBox> #include <QMessageBox>
class RaisedMessageBox : public QMessageBox class RaisedMessageBox final : public QMessageBox
{ {
Q_OBJECT Q_OBJECT

View File

@@ -41,7 +41,7 @@ namespace RSS
class Item; class Item;
} }
class FeedListWidget : public QTreeWidget class FeedListWidget final : public QTreeWidget
{ {
Q_OBJECT Q_OBJECT

View File

@@ -36,7 +36,7 @@ class QNetworkAccessManager;
class QNetworkDiskCache; class QNetworkDiskCache;
class QNetworkReply; class QNetworkReply;
class HtmlBrowser : public QTextBrowser class HtmlBrowser final : public QTextBrowser
{ {
Q_OBJECT Q_OBJECT
@@ -44,7 +44,7 @@ public:
explicit HtmlBrowser(QWidget* parent = nullptr); explicit HtmlBrowser(QWidget* parent = nullptr);
~HtmlBrowser(); ~HtmlBrowser();
virtual QVariant loadResource(int type, const QUrl &name) override; QVariant loadResource(int type, const QUrl &name) override;
protected: protected:
QNetworkAccessManager *m_netManager; QNetworkAccessManager *m_netManager;

View File

@@ -39,7 +39,7 @@ class QTreeView;
class PropertiesWidget; class PropertiesWidget;
class ScanFoldersDelegate : public QItemDelegate class ScanFoldersDelegate final : public QItemDelegate
{ {
Q_OBJECT Q_OBJECT

View File

@@ -48,7 +48,7 @@ namespace Ui
class PluginSelectDialog; class PluginSelectDialog;
} }
class PluginSelectDialog : public QDialog class PluginSelectDialog final : public QDialog
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(PluginSelectDialog) Q_DISABLE_COPY(PluginSelectDialog)

View File

@@ -51,7 +51,7 @@ namespace Ui
class SearchJobWidget; class SearchJobWidget;
} }
class SearchJobWidget : public QWidget class SearchJobWidget final : public QWidget
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(SearchJobWidget) Q_DISABLE_COPY(SearchJobWidget)

View File

@@ -31,7 +31,7 @@
#include <QItemDelegate> #include <QItemDelegate>
class SearchListDelegate : public QItemDelegate class SearchListDelegate final : public QItemDelegate
{ {
Q_OBJECT Q_OBJECT

View File

@@ -32,7 +32,7 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QStringList> #include <QStringList>
class SearchSortModel : public QSortFilterProxyModel class SearchSortModel final : public QSortFilterProxyModel
{ {
using base = QSortFilterProxyModel; using base = QSortFilterProxyModel;

View File

@@ -39,7 +39,7 @@ namespace Ui
class ShutdownConfirmDialog; class ShutdownConfirmDialog;
} }
class ShutdownConfirmDialog : public QDialog class ShutdownConfirmDialog final : public QDialog
{ {
Q_OBJECT Q_OBJECT

View File

@@ -42,7 +42,7 @@ namespace BitTorrent
class TorrentHandle; class TorrentHandle;
} }
class TagFilterModel : public QAbstractListModel class TagFilterModel final : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT

Some files were not shown because too many files have changed in this diff Show More