1
mirror of https://github.com/qbittorrent/qBittorrent synced 2025-10-09 18:32:15 +02:00

Compare commits

...

83 Commits

Author SHA1 Message Date
sledgehammer999
d2f3d1ec2c Bump to 4.0.4 2018-02-16 00:46:34 +02:00
sledgehammer999
bc6e3ae40d Sync translations from Transifex and run lupdate. 2018-02-09 21:14:58 +02:00
sledgehammer999
b02e239f7c Set default file log size to 65 KiB and delete backup logs older than 1 month. 2018-02-09 20:50:43 +02:00
Luís Pereira
397cd4bf60 Don't set application name twice
It's an artifact introduced by commit b3c73b7868.
2018-02-09 20:50:43 +02:00
Thomas Piccirello
409f972ad3 Add missing units 2018-02-09 20:50:42 +02:00
Thomas Piccirello
267961ffca Right-align stat values 2018-02-09 20:50:41 +02:00
Thomas Piccirello
dff753c452 Remove invalid subnets directly from list 2018-02-09 20:50:41 +02:00
Thomas Piccirello
ce3bafd30d Perform ip string validation inside setWebUiAuthSubnetWhitelist 2018-02-09 20:50:40 +02:00
Thomas Piccirello
e5538d9f25 Separate subnet whitelist options into two lines 2018-02-09 20:50:39 +02:00
Tom Piccirello
7a8a32b1c3 Remove default case, fix #8302 2018-02-09 20:50:39 +02:00
Vladimir Golovnev (Glassez)
cac5e0391b Remove legacy and corrupted RSS settings 2018-02-09 20:50:38 +02:00
Vladimir Golovnev (Glassez)
726790fa93 Require '#pragma once' in new code 2018-02-09 20:50:37 +02:00
Thomas Piccirello
c9c7a5be53 Add default case 2018-02-09 20:50:36 +02:00
sledgehammer999
1495513cfc Revert "Remove examples from gpl.html. Closes #7749."
The GPL folks are a bit overzealous. See https://www.gnu.org/licenses/gpl-faq.html#GPLOmitPreamble

This reverts commit 5cf4f00824.
2018-02-09 20:50:36 +02:00
sledgehammer999
085afaac14 Sync translations from Transifex and run lupdate. 2018-02-09 20:50:34 +02:00
Thomas Piccirello
d58a54c758 Use switch statement 2018-02-09 20:50:33 +02:00
Thomas Piccirello
78d9bcb6a1 Match webui statuses to gui, closes #7516 2018-02-09 20:50:33 +02:00
Tom Piccirello
1a43cd329d Only add search separators as needed 2018-02-09 20:50:32 +02:00
Jesse Bryan
2fe687eeca transferlist: added a force reannounce option. closes #6448. 2018-02-09 20:50:31 +02:00
Chocobo1
107bd8a54f Torrent creator: raise maximum piece size to 32 MiB 2018-02-09 20:50:31 +02:00
Chocobo1
865df3fcf1 Fix translation context.
Partially addresses #8220.

Also sort include headers
2018-02-09 20:50:30 +02:00
sledgehammer999
cbf9c52462 Update copyright year. 2018-02-09 20:50:29 +02:00
Chocobo1
171e25e962 Fix translation context. Closes #8211. 2018-02-09 20:50:11 +02:00
sledgehammer999
239d14fd10 Update configure version info. 2018-01-03 23:29:59 +02:00
sledgehammer999
0b7a175156 Update libtorrent bottle for Travis. 2018-01-03 23:28:24 +02:00
Chocobo1
b37dbb60b5 Sort filename lists in .pri files 2018-01-03 23:28:18 +02:00
sledgehammer999
d5acd1f210 configure: Parse all compiler related flags together. 2018-01-03 03:18:15 +02:00
sledgehammer999
65eda4a68e Fix splitting of compiler flags in configure.
Autoconf removes a set of [] during script translation, resulting in a wrong sed command.
2018-01-03 03:18:14 +02:00
sledgehammer999
7209881025 Fix constant status of '[F] Downloading'. Closes #7628. 2018-01-03 03:18:13 +02:00
Chocobo1
a5d0a4b619 Fix column size too narrow on resize
Instead of giving a value, just resize to the content size
2018-01-03 03:18:13 +02:00
Chocobo1
79276a8786 Resize dialog size on high DPI monitors 2018-01-03 03:18:12 +02:00
Chocobo1
fa2b645a64 Fix gui issues on high DPI monitor
Fix LineEdit widget size issues
Up-scale the icons on statusbar
Up-scale the icons in options dialog. Closes #7729.
Fix small icons in cookie manager
Fix progress bar height
Fix small icons in confirm delete dialog
Fix small icons in options dialog
Fix small images in about dialog
2018-01-03 03:18:11 +02:00
Chocobo1
4d5d6df734 Tweak spacing 2018-01-03 03:18:10 +02:00
Chocobo1
2c39b69c18 Cleanup
Use Qt5 connect syntax
Reorder include headers
2018-01-03 03:18:10 +02:00
Chocobo1
13c0077e95 Revert "Run external program" function
This revert df95efe33e partially
2018-01-03 03:18:09 +02:00
Vladimir Golovnev (Glassez)
9299e3f371 Don't process new/updated RSS rules when disabled 2018-01-03 03:18:08 +02:00
Thomas Piccirello
b9ddc6ee86 Use https for www.qbittorrent.org 2018-01-03 03:18:07 +02:00
Thomas Piccirello
276856a614 Add Time Active column 2018-01-03 03:18:07 +02:00
Thomas Piccirello
fbd6a8a0da Add Tags columns 2018-01-03 03:18:06 +02:00
Thomas Piccirello
6fc18b4af6 Reposition Total Size column to match gui 2018-01-03 03:18:05 +02:00
Thomas Piccirello
44d633fb68 Make value formatting consistent with GUI 2017-12-24 03:01:17 +02:00
Thomas Piccirello
eb4bf6cc68 Add "Remaining" and "Availability" columns to webui Content tab 2017-12-24 03:01:15 +02:00
jan.karberg
6db6c850eb search only when category is supported by plugin Closes #8053 2017-12-24 03:01:14 +02:00
thalieht
02ae1e3734 Make peer information flags in peerlist more readable 2017-12-24 03:01:13 +02:00
sledgehammer999
eb887139fd Fix application of speed limits on LAN and μTP connections. Closes #7745. 2017-12-24 03:01:12 +02:00
sledgehammer999
84805f7fb8 Fix natural sorting when the common part of 2 strings ends partially in a number which continues in the uncommon part.
Closes #8080 #6732.
2017-12-24 03:01:11 +02:00
sledgehammer999
2719131ed2 Simplify sorting code. 2017-12-24 03:01:10 +02:00
sledgehammer999
52401bd2b0 Fix sorting of country flags column in Peers tab. 2017-12-24 03:01:09 +02:00
Vladimir Golovnev (Glassez)
4834703bc4 Fix RSS rule updated when deleting
Closes #8094
2017-12-24 03:01:08 +02:00
Chocobo1
3ed73244b1 Use standard folder icon for open file behavior on Windows. Closes #7880. 2017-12-24 03:01:07 +02:00
vit9696
97cd430125 Fix cmd+w not closing the main window on macOS 2017-12-24 03:01:06 +02:00
vit9696
d202b85d51 Fix Finder reveal in preview and torrent contents 2017-12-24 03:01:05 +02:00
vit9696
c51b79e9fc Put macOS specific functions to MacUtils namespace 2017-12-24 03:01:04 +02:00
vit9696
4449018207 Fix torrent file selection in Finder on mac 2017-12-24 03:01:03 +02:00
Chocobo1
ced8e41473 Add source field in Torrent creator. Closes #7965.
This field is often used for cross-seeding between (private) trackers.
2017-12-24 03:01:02 +02:00
Chocobo1
2c66ed6708 Add struct TorrentCreatorParams for passing parameters
Filter out continuous newlines in Trackers field
Avoid adding empty url seed entries

Cleanup:
  Replace boost::bind
  Add const
  Use nullptr
  Use QString::SkipEmptyParts
  Rename variables
  Throw proper exception type
2017-12-24 03:01:01 +02:00
Chocobo1
c7d3d6ac90 WebUI: Only prepend scheme when it is not present. Closes #8057. 2017-12-24 03:00:55 +02:00
sledgehammer999
13210b3e9f Bump to 4.0.3 2017-12-17 23:31:59 +02:00
sledgehammer999
6e622fc23b Sync translations from Transifex and run lupdate. 2017-12-17 23:19:17 +02:00
Vladimir Golovnev (Glassez)
ae35111b59 Fix WebUI is not reachable via IPv6 2017-12-17 23:07:59 +02:00
Chocobo1
e1c3d419a7 Disable the "?" help button in all dialogs on Windows. Closes #7365. 2017-12-17 23:07:58 +02:00
Chocobo1
7396b8adba Partial revert eac8838dc2. Fixes #7952.
mapFromSource() didn't work as expected, when used in lessThan(), it sometimes
returns an invalid QModelIndex.
A crash is observed when filtering source model via filterAcceptsRow() in #7952,
the crash is due to endless recursive of filterAcceptsRow() & lessThan() calling
each other and mapFromSource() is the culprit of it.
2017-12-17 23:07:57 +02:00
Chocobo1
c09001545d Allow to drag-n-drop URLs into mainwindow to initiate download
Fix issue: https://github.com/qbittorrent/qBittorrent/issues/7785#issuecomment-347092418
Minor refactor
2017-12-17 23:07:55 +02:00
Chocobo1
f8d4315f7e [WebUI] Use POST for logout command
This is to avoid browser being smart to prefetch the link then logging
out the user.
2017-12-17 23:07:54 +02:00
Chocobo1
1fa2957d27 [WebUI] Add check to avoid type error after logout 2017-12-17 23:07:53 +02:00
Chocobo1
ade50d2b53 Fix missing qbt logo on login page in webUI. Closes #7953. 2017-12-17 23:07:52 +02:00
sledgehammer999
0fa1d35b87 Add height padding to the transfer list icons. Closes #7951. 2017-12-17 23:07:29 +02:00
sledgehammer999
6486fc5f4d Bump to 4.0.2 2017-12-01 01:59:53 +02:00
sledgehammer999
1e059ab1a2 Sync translations from Transifex and run lupdate. 2017-12-01 01:44:49 +02:00
Mike Tzou
15b137211b [GUI] Implement stable sort (#7703)
* NaturalCompare now returns compare result instead of "less than" result
* Change to stable sort in GUI components
* Add Utils::String::naturalLessThan() helper function
* Use Qt::CaseSensitivity type
2017-12-01 01:39:16 +02:00
thalieht
6f8f1d7bad Coding style for many files 2017-12-01 01:39:15 +02:00
sledgehammer999
a31f0c0a3d Stop logging IP filter parsing errors after a while.
When a lot of errors happen the resulting log lines introduce
a huge slowdown of the GUI, due to writing each log line to disk.

Closes #7755.
2017-12-01 01:39:08 +02:00
Evgeny Lensky
f977d1293a Fix i386 build (configure)
Fix building on i686 fails with wrong multilib library path #7845

and rebregenerate ./configure
2017-11-27 19:16:03 +02:00
sledgehammer999
1399be50cb Fix crash on some systems when creating address object for 255.255.255.255
Closes #7735.
2017-11-27 19:14:41 +02:00
Vladimir Golovnev (Glassez)
52dcf32cc8 Implement Import/Export RSS rules in JSON format 2017-11-27 19:14:40 +02:00
Vladimir Golovnev (Glassez)
52b2b807ab Implement Import/Export RSS rules in legacy format 2017-11-27 19:14:38 +02:00
sledgehammer999
5cf4f00824 Remove examples from gpl.html. Closes #7749. 2017-11-27 19:14:37 +02:00
TheNicker
faa6fad025 Fixed blurry text under Windows by setting DPI awareness to default 2017-11-27 19:14:36 +02:00
Vladimir Golovnev (Glassez)
9f94bbce3a Fix RSS Parser
Closes #7751
Closes #7763
Closes #7768
Closes #7786
2017-11-27 19:14:35 +02:00
Chocobo1
5c49b2486c Change MixedModeAlgorithm default to TCP. Closes #7779.
MixedModeAlgorithm::Proportional will throttle TCP connections when utp
is in use and users is expecting maximum speed no matter what, so now
disable the throttling.
2017-11-27 19:14:34 +02:00
thoradia
4f6e7f97c6 Fix missing include in rss_feed.cpp
Fixes #7805.
2017-11-27 19:14:33 +02:00
sledgehammer999
7751c5b75c Merge pull request #7835 from heirecka/v4_0_x
Fix build with --disable-webui
2017-11-23 01:50:21 +02:00
Heiko Becker
a1a9f3317b Fix build with --disable-webui
"app/application.cpp:108:7: error: class 'Application' does not have
any field named 'm_webui'"
2017-11-22 21:30:08 +01:00
218 changed files with 49403 additions and 45346 deletions

View File

@@ -76,10 +76,13 @@ before_install:
- shopt -s expand_aliases
- alias make="colormake -j3" # Using nprocs/2 sometimes may fail (gcc is killed by system)
#- libt_path="$HOME/libt_install"
#- ltconf="$ltconf --prefix="$libt_path" --disable-geoip"
- qbt_path="$HOME/qbt_install"
- qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH="$libt_path/lib/pkgconfig":/opt/qt55/lib/pkgconfig:$PKG_CONFIG_PATH"
- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH=/opt/qt55/lib/pkgconfig:$PKG_CONFIG_PATH"
else
qbtconf="$qbtconf --prefix="$qbt_path""
fi
# options for specific branches
- if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ; fi
@@ -131,13 +134,13 @@ install:
cp "version" $HOME/hombebrew_cache
cd "$HOME/hombebrew_cache"
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.0.11+git20170910.6d5625e0ea.el_capitan.bottle.tar.gz
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.1.6+git20180101.b45acf28a5+patched-configure.el_capitan.bottle.tar.gz
fi
# Copy custom libtorrent bottle to homebrew's cache so it can find and install it
# Also install our custom libtorrent formula by passing the local path to it
# These 2 files are restored from Travis' cache.
cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.0.11+git20170910.6d5625e0ea.el_capitan.bottle.tar.gz" "$(brew --cache)"
cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.1.6+git20180101.b45acf28a5+patched-configure.el_capitan.bottle.tar.gz" "$(brew --cache)"
brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb"
if [ "$build_system" = "cmake" ]; then
@@ -168,11 +171,15 @@ script:
BUILD_TOOL="ninja"
fi
if [ "$build_system" = "qmake" ]; then
./bootstrap.sh && ./configure $qbtconf
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
# For some reason for RC_1_1 we need to also specify the OpenSSL compiler/linker flags
# Homebrew doesn't symlink OpenSSL for security reasons
./bootstrap.sh && ./configure $qbtconf CXXFLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --cflags openssl)" LDFLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --libs openssl)"
sed -i "" -e "s/^\(CC.*&&\).*$/\1 $CC/" src/Makefile # workaround for Qt & ccache: https://bugreports.qt.io/browse/QTBUG-31034
sed -i "" -e "s/^\(CXX.*&&\).*$/\1 $CXX/" src/Makefile
sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile
else
./bootstrap.sh && ./configure $qbtconf
fi
BUILD_TOOL="make"
fi

View File

@@ -240,7 +240,23 @@ Example:
```
### 9. Misc. ###
### 9. Include guard. ###
`#pragma once` should be used instead of "include guard" in new code:
```c++
// examplewidget.h
#pragma once
#include <QWidget>
class ExampleWidget : public QWidget
{
// (some code omitted)
};
```
### 10. Misc. ###
* Line breaks for long lines with operation:

View File

@@ -1,3 +1,65 @@
* Fri Feb 16 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.4
- FEATURE: Add source field in Torrent creator. Closes #7965. (Chocobo1)
- FEATURE: Torrent creator: raise maximum piece size to 32 MiB (Chocobo1)
- FEATURE: Add a force reannounce option in the transfer list context menu. Closes #6448. (Jesse Bryan)
- BUGFIX: Fix sorting of country flags column in Peers tab. (sledgehammer999)
- BUGFIX: Fix natural sorting when the common part of 2 strings ends partially in a number which continues in the uncommon part. Closes #8080 #6732. (sledgehammer999)
- BUGFIX: Fix application of speed limits on LAN and μTP connections. Closes #7745. (sledgehammer999)
- BUGFIX: Make peer information flags in peerlist more readable. (thalieht)
- BUGFIX: Fix gui issues on high DPI monitor. (Chocobo1)
- BUGFIX: Fix dialog and column size on high DPI monitors. (Chocobo1)
- BUGFIX: Fix constant status of '[F] Downloading'. Closes #7628. (sledgehammer999)
- BUGFIX: Fix translation context. Closes #8211. (sledgehammer999)
- BUGFIX: Separate subnet whitelist options into two lines. (Thomas Piccirello)
- BUGFIX: Don't set application name twice. (Luís Pereira)
- BUGFIX: Set default file log size to 65 KiB and delete backup logs older than 1 month. (sledgehammer999)
- WEBUI: Only prepend scheme when it is not present. Closes #8057. (Chocobo1)
- WEBUI: Add "Remaining" and "Availability" columns to webui Content tab. (Thomas Piccirello)
- WEBUI: Make value formatting consistent with GUI (Thomas Piccirello)
- WEBUI: Reposition Total Size column to match gui (Thomas Piccirello)
- WEBUI: Add Tags and Time Active columns (Thomas Piccirello)
- WEBUI: Use https for www.qbittorrent.org (Thomas Piccirello)
- WEBUI: Match webui statuses to gui, closes #7516 (Thomas Piccirello)
- WEBUI: Right-align stat values (Thomas Piccirello)
- WEBUI: Add missing units. (Thomas Piccirello)
- RSS: Fix crash when deleting rule because it tries to update. Closes #8094 (glassez)
- RSS: Don't process new/updated RSS rules when disabled (glassez)
- RSS: Remove legacy and corrupted RSS settings (glassez)
- SEARCH: Search only when category is supported by plugin. Closes #8053. (jan.karberg)
- SEARCH: Only add search separators as needed. (Thomas Piccirello)
- COSMETIC: Tweak spacing in torrent properties widget and speed widget. (Chocobo1)
- WINDOWS: Use standard folder icon for open file behavior on Windows. Closes #7880. (Chocobo1)
- WINDOWS: Revert "Run external program" function. Now you will not be able to directly run batch scripts. (Chocobo1)
- MACOS: Fix torrent file selection in Finder on mac (vit9696)
- MACOS: Fix Finder reveal in preview and torrent contents (vit9696)
- MACOS: Fix cmd+w not closing the main window on macOS (vit9696)
- OTHER: Fix splitting of compiler flags in configure. Autoconf removes a set of [] during script translation, resulting in a wrong sed command. (sledgehammer999)
- OTHER: configure: Parse all compiler related flags together. (sledgehammer999)
- OTHER: Update copyright year. (sledgehammer999)
* Sun Dec 17 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.3
- BUGFIX: Add height padding to the transfer list icons. Closes #7951. (sledgehammer999)
- BUGFIX: Allow to drag-n-drop URLs into mainwindow to initiate download. (Chocobo1)
- BUGFIX: Fix crash when fitlering search results. Stable sorting is removed. Closes #7952.(Chocobo1)
- WEBUI: Fix missing qbt logo on login page in webUI. Closes #7953. (Chocobo1)
- WEBUI: Add check to avoid type error after logout. (Chocobo1)
- WEBUI: Use POST for logout command. This is to avoid browser being smart to prefetch the link then logging out the user. (Chocobo1)
- WEBUI: Fix WebUI is not reachable via IPv6. (glassez)
- WINDOWS: Disable the "?" help button in all dialogs on Windows. Closes #7365. Requires Qt 5.10. (Chocobo1)
* Fri Dec 01 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.2
- BUGFIX: Fix crash on some systems when creating address object for 255.255.255.255. Closes #7735. (sledgehammer999)
- PERFORMANCE: Change MixedModeAlgorithm default to TCP. This was the v3_3_x default and should sustain higher speeds. Closes #7779. (Chocobo1)
- PERFORMANCE: Stop logging IP filter parsing errors after a while, otherwise the GUI freezes or qBittorrent doesn't start. (sledgehammer999)
- GUI: Implement stable sort. Rows in transfer list shouldn't flicker anymore. (Chocobo1)
- WEBUI: Fix build when webui is disabled. (Heiko Becker)
- RSS: Fix build because of missing header. Closes #7805. (thoradia)
- RSS: Fix RSS parser. (glassez)
- RSS: Implement Import/Export RSS rules in legacy(aka v3_3_x) format. (glassez)
- RSS: Implement Import/Export RSS rules in JSON format. (glassez)
- WINDOWS: Fixed blurry text under Windows by setting DPI awareness to default. (TheNicker)
- LINUX: Fix i386 build. (Evgeny Lensky)
* Wed Nov 22 2017 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.1
- BUGFIX: Fix crash on opening torrent/magnet (uninitialized pointer). Closes #7739 #7723. (sledgehammer999)
- BUGFIX: Enable preferences Apply button when ip banlist is modified (Thomas Piccirello)

View File

@@ -35,7 +35,7 @@ You can also download it from [here](https://github.com/qbittorrent/qBittorrent/
### Misc:
For more information please visit:
http://www.qbittorrent.org
https://www.qbittorrent.org
or our wiki here:
http://wiki.qbittorrent.org

40
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for qbittorrent v3.2.0alpha.
# Generated by GNU Autoconf 2.69 for qbittorrent v4.0.4.
#
# Report bugs to <bugs.qbittorrent.org>.
#
@@ -580,10 +580,10 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='qbittorrent'
PACKAGE_TARNAME='qbittorrent'
PACKAGE_VERSION='v3.2.0alpha'
PACKAGE_STRING='qbittorrent v3.2.0alpha'
PACKAGE_VERSION='v4.0.4'
PACKAGE_STRING='qbittorrent v4.0.4'
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
PACKAGE_URL='http://www.qbittorrent.org/'
PACKAGE_URL='https://www.qbittorrent.org/'
ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
@@ -1296,7 +1296,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures qbittorrent v3.2.0alpha to adapt to many kinds of systems.
\`configure' configures qbittorrent v4.0.4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1367,7 +1367,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of qbittorrent v3.2.0alpha:";;
short | recursive ) echo "Configuration of qbittorrent v4.0.4:";;
esac
cat <<\_ACEOF
@@ -1438,7 +1438,7 @@ Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
Report bugs to <bugs.qbittorrent.org>.
qbittorrent home page: <http://www.qbittorrent.org/>.
qbittorrent home page: <https://www.qbittorrent.org/>.
_ACEOF
ac_status=$?
fi
@@ -1501,7 +1501,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
qbittorrent configure v3.2.0alpha
qbittorrent configure v4.0.4
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1640,7 +1640,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by qbittorrent $as_me v3.2.0alpha, which was
It was created by qbittorrent $as_me v4.0.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3818,7 +3818,7 @@ fi
# Define the identity of the package.
PACKAGE='qbittorrent'
VERSION='v3.2.0alpha'
VERSION='v4.0.4'
cat >>confdefs.h <<_ACEOF
@@ -4705,9 +4705,8 @@ fi
libsubdirs="lib64 libx32 lib lib64" ;; #(
ppc64|s390x|sparc64|aarch64|ppc64le) :
libsubdirs="lib64 lib lib64" ;; #(
libsubdirs="lib") :
;; #(
*) :
libsubdirs="lib"
;;
esac
@@ -5502,7 +5501,7 @@ extract() {
new_line='
'
# Convert " -" to "\n" if not between quotes and remove possible leading white spaces
string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[:space:]*//')
string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[[:space:]]*//')
SAVEIFS=$IFS
IFS=$(printf "\n\b")
for i in $string; do
@@ -5516,9 +5515,8 @@ extract() {
IFS=$SAVEIFS
}
extract $CPPFLAGS
extract "$CFLAGS $CPPFLAGS $CXXFLAGS"
QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
QBT_CONF_EXTRA_CFLAGS="$QBT_CONF_EXTRA_CFLAGS $CXXFLAGS"
# Substitute the values of these vars in conf.pri.in
@@ -6100,7 +6098,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v3.2.0alpha, which was
This file was extended by qbittorrent $as_me v4.0.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6152,13 +6150,13 @@ Configuration commands:
$config_commands
Report bugs to <bugs.qbittorrent.org>.
qbittorrent home page: <http://www.qbittorrent.org/>."
qbittorrent home page: <https://www.qbittorrent.org/>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
qbittorrent config.status v3.2.0alpha
qbittorrent config.status v4.0.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -7415,7 +7413,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v3.2.0alpha, which was
This file was extended by qbittorrent $as_me v4.0.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7467,13 +7465,13 @@ Configuration commands:
$config_commands
Report bugs to <bugs.qbittorrent.org>.
qbittorrent home page: <http://www.qbittorrent.org/>."
qbittorrent home page: <https://www.qbittorrent.org/>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
qbittorrent config.status v3.2.0alpha
qbittorrent config.status v4.0.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View File

@@ -1,4 +1,4 @@
AC_INIT([qbittorrent], [v3.2.0alpha], [bugs.qbittorrent.org], [], [http://www.qbittorrent.org/])
AC_INIT([qbittorrent], [v4.0.4], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
@@ -190,7 +190,7 @@ extract() {
new_line='
'
# Convert " -" to "\n" if not between quotes and remove possible leading white spaces
string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[[:space:]]*//')
string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[[[:space:]]]*//')
SAVEIFS=$IFS
IFS=$(printf "\n\b")
for i in $string; do
@@ -204,9 +204,8 @@ extract() {
IFS=$SAVEIFS
}
extract $CPPFLAGS
extract "$CFLAGS $CPPFLAGS $CXXFLAGS"
QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
QBT_CONF_EXTRA_CFLAGS="$QBT_CONF_EXTRA_CFLAGS $CXXFLAGS"
# Substitute the values of these vars in conf.pri.in
AC_SUBST(QBT_CONF_INCLUDES)

4
dist/mac/Info.plist vendored
View File

@@ -45,7 +45,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.0.1</string>
<string>4.0.4</string>
<key>CFBundleSignature</key>
<string>qBit</string>
<key>CFBundleExecutable</key>
@@ -59,7 +59,7 @@
<key>NSAppleScriptEnabled</key>
<string>YES</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2006-2017 The qBittorrent project</string>
<string>Copyright © 2006-2018 The qBittorrent project</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>

View File

@@ -56,7 +56,7 @@
</image>
</screenshot>
</screenshots>
<url type="homepage">http://www.qbittorrent.org/</url>
<url type="homepage">https://www.qbittorrent.org/</url>
<update_contact>sledgehammer999@qbittorrent.org</update_contact>
<developer_name>The qBittorrent Project</developer_name>
<url type="bugtracker">http://bugs.qbittorrent.org/</url>

View File

@@ -86,7 +86,7 @@ Section $(inst_qbt_req) ;"qBittorrent (required)"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "UninstallString" '"$INSTDIR\uninst.exe"'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "DisplayIcon" '"$INSTDIR\qbittorrent.exe",0'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "Publisher" "The qBittorrent project"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "URLInfoAbout" "http://www.qbittorrent.org"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "URLInfoAbout" "https://www.qbittorrent.org"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "DisplayVersion" "${PROG_VERSION}"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qBittorrent" "NoRepair" 1

View File

@@ -27,7 +27,7 @@ XPStyle on
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
; Program specific
!define PROG_VERSION "4.0.1"
!define PROG_VERSION "4.0.4"
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
@@ -50,7 +50,7 @@ XPStyle on
;Installer Version Information
VIAddVersionKey "ProductName" "qBittorrent"
VIAddVersionKey "CompanyName" "The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2017 The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2018 The qBittorrent project"
VIAddVersionKey "FileDescription" "qBittorrent - A Bittorrent Client"
VIAddVersionKey "FileVersion" "${PROG_VERSION}"

View File

@@ -2,4 +2,4 @@
Translations = translations
[Platforms]
WindowsArguments = dpiawareness=1
;WindowsArguments = dpiawareness=1

View File

@@ -114,7 +114,7 @@ AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[
AS_CASE([${host_cpu}],
[x86_64],[libsubdirs="lib64 libx32 lib lib64"],
[ppc64|s390x|sparc64|aarch64|ppc64le],[libsubdirs="lib64 lib lib64"],
[libsubdirs="lib"],
[libsubdirs="lib"]
)
dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give

View File

@@ -87,7 +87,7 @@ namespace
const QString KEY_FILELOGGER_PATH = FILELOGGER_SETTINGS_KEY("Path");
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
const QString KEY_FILELOGGER_DELETEOLD = FILELOGGER_SETTINGS_KEY("DeleteOld");
const QString KEY_FILELOGGER_MAXSIZE = FILELOGGER_SETTINGS_KEY("MaxSize");
const QString KEY_FILELOGGER_MAXSIZEBYTES = FILELOGGER_SETTINGS_KEY("MaxSizeBytes");
const QString KEY_FILELOGGER_AGE = FILELOGGER_SETTINGS_KEY("Age");
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
@@ -98,6 +98,10 @@ namespace
const char PARAMS_SEPARATOR[] = "|";
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QLatin1String("profile");
const int MIN_FILELOG_SIZE = 1024; // 1KiB
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
const int DEFAULT_FILELOG_SIZE = 65 * 1024; // 65KiB
}
Application::Application(const QString &id, int &argc, char **argv)
@@ -105,7 +109,9 @@ Application::Application(const QString &id, int &argc, char **argv)
, m_running(false)
, m_shutdownAct(ShutdownDialogAction::Exit)
, m_commandLineArgs(parseCommandLine(this->arguments()))
#ifndef DISABLE_WEBUI
, m_webui(nullptr)
#endif
{
qRegisterMetaType<Log::Msg>("Log::Msg");
@@ -126,7 +132,6 @@ Application::Application(const QString &id, int &argc, char **argv)
if (m_commandLineArgs.webUiPort > 0) // it will be -1 when user did not set any value
Preferences::instance()->setWebUiPort(m_commandLineArgs.webUiPort);
setApplicationName("qBittorrent");
initializeTranslation();
#if !defined(DISABLE_GUI)
@@ -219,29 +224,22 @@ void Application::setFileLoggerDeleteOld(bool value)
int Application::fileLoggerMaxSize() const
{
int val = settings()->loadValue(KEY_FILELOGGER_MAXSIZE, 10).toInt();
if (val < 1)
return 1;
if (val > 1000)
return 1000;
return val;
int val = settings()->loadValue(KEY_FILELOGGER_MAXSIZEBYTES, DEFAULT_FILELOG_SIZE).toInt();
return std::min(std::max(val, MIN_FILELOG_SIZE), MAX_FILELOG_SIZE);
}
void Application::setFileLoggerMaxSize(const int value)
void Application::setFileLoggerMaxSize(const int bytes)
{
int clampedValue = std::min(std::max(bytes, MIN_FILELOG_SIZE), MAX_FILELOG_SIZE);
if (m_fileLogger)
m_fileLogger->setMaxSize(value);
settings()->storeValue(KEY_FILELOGGER_MAXSIZE, std::min(std::max(value, 1), 1000));
m_fileLogger->setMaxSize(clampedValue);
settings()->storeValue(KEY_FILELOGGER_MAXSIZEBYTES, clampedValue);
}
int Application::fileLoggerAge() const
{
int val = settings()->loadValue(KEY_FILELOGGER_AGE, 6).toInt();
if (val < 1)
return 1;
if (val > 365)
return 365;
return val;
int val = settings()->loadValue(KEY_FILELOGGER_AGE, 1).toInt();
return std::min(std::max(val, 1), 365);
}
void Application::setFileLoggerAge(const int value)
@@ -289,27 +287,6 @@ void Application::runExternalProgram(BitTorrent::TorrentHandle *const torrent) c
#if defined(Q_OS_UNIX)
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
#elif defined(Q_OS_WIN) // test cmd: `echo "%F" > "c:\ab ba.txt"`
program.prepend(QLatin1String("\"")).append(QLatin1String("\""));
program.prepend(Utils::Misc::windowsSystemPath() + QLatin1String("\\cmd.exe /C "));
const int cmdMaxLength = 32768; // max length (incl. terminate char) for `lpCommandLine` in `CreateProcessW()`
if ((program.size() + 1) > cmdMaxLength) {
logger->addMessage(tr("Torrent: %1, run external program command too long (length > %2), execution failed.").arg(torrent->name()).arg(cmdMaxLength), Log::CRITICAL);
return;
}
STARTUPINFOW si = {0};
si.cb = sizeof(si);
PROCESS_INFORMATION pi = {0};
WCHAR *arg = new WCHAR[program.size() + 1];
program.toWCharArray(arg);
arg[program.size()] = L'\0';
if (CreateProcessW(NULL, arg, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
delete[] arg;
#else
QProcess::startDetached(program);
#endif

View File

@@ -101,7 +101,7 @@ public:
bool isFileLoggerDeleteOld() const;
void setFileLoggerDeleteOld(bool value);
int fileLoggerMaxSize() const;
void setFileLoggerMaxSize(const int value);
void setFileLoggerMaxSize(const int bytes);
int fileLoggerAge() const;
void setFileLoggerAge(const int value);
int fileLoggerAgeType() const;

View File

@@ -135,7 +135,7 @@ void FileLogger::addLogMessage(const Log::Msg &msg)
str << QDateTime::fromMSecsSinceEpoch(msg.timestamp).toString(Qt::ISODate) << " - " << msg.message << endl;
if (m_backup && (m_logFile->size() >= (m_maxSize * 1024 * 1024))) {
if (m_backup && (m_logFile->size() >= m_maxSize)) {
closeLogFile();
int counter = 0;
QString backupLogFilename = m_path + ".bak";

View File

@@ -212,6 +212,10 @@ int main(int argc, char *argv[])
// 3. https://bugreports.qt.io/browse/QTBUG-46015
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
// this is the default in Qt6
app->setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif
#endif
#if defined(Q_OS_MAC)

View File

@@ -1,124 +1,124 @@
HEADERS += \
$$PWD/asyncfilestorage.h \
$$PWD/types.h \
$$PWD/tristatebool.h \
$$PWD/bittorrent/addtorrentparams.h \
$$PWD/bittorrent/cachestatus.h \
$$PWD/bittorrent/infohash.h \
$$PWD/bittorrent/magneturi.h \
$$PWD/bittorrent/peerinfo.h \
$$PWD/bittorrent/private/bandwidthscheduler.h \
$$PWD/bittorrent/private/filterparserthread.h \
$$PWD/bittorrent/private/resumedatasavingmanager.h \
$$PWD/bittorrent/private/speedmonitor.h \
$$PWD/bittorrent/private/statistics.h \
$$PWD/bittorrent/session.h \
$$PWD/bittorrent/sessionstatus.h \
$$PWD/bittorrent/torrentcreatorthread.h \
$$PWD/bittorrent/torrenthandle.h \
$$PWD/bittorrent/torrentinfo.h \
$$PWD/bittorrent/tracker.h \
$$PWD/bittorrent/trackerentry.h \
$$PWD/filesystemwatcher.h \
$$PWD/logger.h \
$$PWD/settingsstorage.h \
$$PWD/settingvalue.h \
$$PWD/preferences.h \
$$PWD/indexrange.h \
$$PWD/iconprovider.h \
$$PWD/http/irequesthandler.h \
$$PWD/global.h \
$$PWD/http/connection.h \
$$PWD/http/irequesthandler.h \
$$PWD/http/requestparser.h \
$$PWD/http/responsebuilder.h \
$$PWD/http/responsegenerator.h \
$$PWD/http/server.h \
$$PWD/http/types.h \
$$PWD/http/responsebuilder.h \
$$PWD/iconprovider.h \
$$PWD/indexrange.h \
$$PWD/logger.h \
$$PWD/net/dnsupdater.h \
$$PWD/net/downloadmanager.h \
$$PWD/net/downloadhandler.h \
$$PWD/net/downloadmanager.h \
$$PWD/net/geoipmanager.h \
$$PWD/net/portforwarder.h \
$$PWD/net/private/geoipdatabase.h \
$$PWD/net/proxyconfigurationmanager.h \
$$PWD/net/reverseresolution.h \
$$PWD/net/smtp.h \
$$PWD/net/private/geoipdatabase.h \
$$PWD/bittorrent/addtorrentparams.h \
$$PWD/bittorrent/infohash.h \
$$PWD/bittorrent/session.h \
$$PWD/bittorrent/sessionstatus.h \
$$PWD/bittorrent/cachestatus.h \
$$PWD/bittorrent/magneturi.h \
$$PWD/bittorrent/torrentinfo.h \
$$PWD/bittorrent/torrenthandle.h \
$$PWD/bittorrent/peerinfo.h \
$$PWD/bittorrent/trackerentry.h \
$$PWD/bittorrent/tracker.h \
$$PWD/bittorrent/torrentcreatorthread.h \
$$PWD/bittorrent/private/speedmonitor.h \
$$PWD/bittorrent/private/bandwidthscheduler.h \
$$PWD/bittorrent/private/filterparserthread.h \
$$PWD/bittorrent/private/statistics.h \
$$PWD/bittorrent/private/resumedatasavingmanager.h \
$$PWD/preferences.h \
$$PWD/private/profile_p.h \
$$PWD/profile.h \
$$PWD/rss/private/rss_parser.h \
$$PWD/rss/rss_article.h \
$$PWD/rss/rss_item.h \
$$PWD/rss/rss_feed.h \
$$PWD/rss/rss_folder.h \
$$PWD/rss/rss_session.h \
$$PWD/rss/rss_autodownloader.h \
$$PWD/rss/rss_autodownloadrule.h \
$$PWD/rss/private/rss_parser.h \
$$PWD/rss/rss_feed.h \
$$PWD/rss/rss_folder.h \
$$PWD/rss/rss_item.h \
$$PWD/rss/rss_session.h \
$$PWD/scanfoldersmodel.h \
$$PWD/searchengine.h \
$$PWD/settingsstorage.h \
$$PWD/settingvalue.h \
$$PWD/torrentfileguard.h \
$$PWD/torrentfilter.h \
$$PWD/tristatebool.h \
$$PWD/types.h \
$$PWD/unicodestrings.h \
$$PWD/utils/fs.h \
$$PWD/utils/gzip.h \
$$PWD/utils/misc.h \
$$PWD/utils/net.h \
$$PWD/utils/random.h \
$$PWD/utils/string.h \
$$PWD/utils/version.h \
$$PWD/profile.h \
$$PWD/private/profile_p.h \
$$PWD/unicodestrings.h \
$$PWD/torrentfileguard.h \
$$PWD/torrentfilter.h \
$$PWD/scanfoldersmodel.h \
$$PWD/searchengine.h \
$$PWD/global.h
$$PWD/utils/version.h
SOURCES += \
$$PWD/asyncfilestorage.cpp \
$$PWD/tristatebool.cpp \
$$PWD/bittorrent/infohash.cpp \
$$PWD/bittorrent/magneturi.cpp \
$$PWD/bittorrent/peerinfo.cpp \
$$PWD/bittorrent/private/bandwidthscheduler.cpp \
$$PWD/bittorrent/private/filterparserthread.cpp \
$$PWD/bittorrent/private/resumedatasavingmanager.cpp \
$$PWD/bittorrent/private/speedmonitor.cpp \
$$PWD/bittorrent/private/statistics.cpp \
$$PWD/bittorrent/session.cpp \
$$PWD/bittorrent/torrentcreatorthread.cpp \
$$PWD/bittorrent/torrenthandle.cpp \
$$PWD/bittorrent/torrentinfo.cpp \
$$PWD/bittorrent/tracker.cpp \
$$PWD/bittorrent/trackerentry.cpp \
$$PWD/filesystemwatcher.cpp \
$$PWD/logger.cpp \
$$PWD/settingsstorage.cpp \
$$PWD/preferences.cpp \
$$PWD/iconprovider.cpp \
$$PWD/http/connection.cpp \
$$PWD/http/requestparser.cpp \
$$PWD/http/responsebuilder.cpp \
$$PWD/http/responsegenerator.cpp \
$$PWD/http/server.cpp \
$$PWD/http/responsebuilder.cpp \
$$PWD/iconprovider.cpp \
$$PWD/logger.cpp \
$$PWD/net/dnsupdater.cpp \
$$PWD/net/downloadmanager.cpp \
$$PWD/net/downloadhandler.cpp \
$$PWD/net/downloadmanager.cpp \
$$PWD/net/geoipmanager.cpp \
$$PWD/net/portforwarder.cpp \
$$PWD/net/private/geoipdatabase.cpp \
$$PWD/net/proxyconfigurationmanager.cpp \
$$PWD/net/reverseresolution.cpp \
$$PWD/net/smtp.cpp \
$$PWD/net/private/geoipdatabase.cpp \
$$PWD/bittorrent/infohash.cpp \
$$PWD/bittorrent/session.cpp \
$$PWD/bittorrent/magneturi.cpp \
$$PWD/bittorrent/torrentinfo.cpp \
$$PWD/bittorrent/torrenthandle.cpp \
$$PWD/bittorrent/peerinfo.cpp \
$$PWD/bittorrent/trackerentry.cpp \
$$PWD/bittorrent/tracker.cpp \
$$PWD/bittorrent/torrentcreatorthread.cpp \
$$PWD/bittorrent/private/speedmonitor.cpp \
$$PWD/bittorrent/private/bandwidthscheduler.cpp \
$$PWD/bittorrent/private/filterparserthread.cpp \
$$PWD/bittorrent/private/statistics.cpp \
$$PWD/bittorrent/private/resumedatasavingmanager.cpp \
$$PWD/preferences.cpp \
$$PWD/private/profile_p.cpp \
$$PWD/profile.cpp \
$$PWD/rss/private/rss_parser.cpp \
$$PWD/rss/rss_article.cpp \
$$PWD/rss/rss_item.cpp \
$$PWD/rss/rss_feed.cpp \
$$PWD/rss/rss_folder.cpp \
$$PWD/rss/rss_session.cpp \
$$PWD/rss/rss_autodownloader.cpp \
$$PWD/rss/rss_autodownloadrule.cpp \
$$PWD/rss/private/rss_parser.cpp \
$$PWD/rss/rss_feed.cpp \
$$PWD/rss/rss_folder.cpp \
$$PWD/rss/rss_item.cpp \
$$PWD/rss/rss_session.cpp \
$$PWD/scanfoldersmodel.cpp \
$$PWD/searchengine.cpp \
$$PWD/settingsstorage.cpp \
$$PWD/torrentfileguard.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/tristatebool.cpp \
$$PWD/utils/fs.cpp \
$$PWD/utils/gzip.cpp \
$$PWD/utils/misc.cpp \
$$PWD/utils/net.cpp \
$$PWD/utils/random.cpp \
$$PWD/utils/string.cpp \
$$PWD/profile.cpp \
$$PWD/private/profile_p.cpp \
$$PWD/torrentfileguard.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/scanfoldersmodel.cpp \
$$PWD/searchengine.cpp
$$PWD/utils/string.cpp

View File

@@ -273,18 +273,20 @@ qreal PeerInfo::relevance() const
void PeerInfo::determineFlags()
{
QStringList flagsDescriptionList;
if (isInteresting()) {
// d = Your client wants to download, but peer doesn't want to send (interested and choked)
if (isRemoteChocked()) {
m_flags += "d ";
m_flagsDescription += tr("interested(local) and choked(peer)");
m_flagsDescription += ", ";
flagsDescriptionList += "d = "
+ tr("Interested(local) and Choked(peer)");
}
else {
// D = Currently downloading (interested and not choked)
m_flags += "D ";
m_flagsDescription += tr("interested(local) and unchoked(peer)");
m_flagsDescription += ", ";
flagsDescriptionList += "D = "
+ tr("interested(local) and unchoked(peer)");
}
}
@@ -292,97 +294,95 @@ void PeerInfo::determineFlags()
// u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
if (isChocked()) {
m_flags += "u ";
m_flagsDescription += tr("interested(peer) and choked(local)");
m_flagsDescription += ", ";
flagsDescriptionList += "u = "
+ tr("interested(peer) and choked(local)");
}
else {
// U = Currently uploading (interested and not choked)
m_flags += "U ";
m_flagsDescription += tr("interested(peer) and unchoked(local)");
m_flagsDescription += ", ";
flagsDescriptionList += "U = "
+ tr("interested(peer) and unchoked(local)");
}
}
// O = Optimistic unchoke
if (optimisticUnchoke()) {
m_flags += "O ";
m_flagsDescription += tr("optimistic unchoke");
m_flagsDescription += ", ";
flagsDescriptionList += "O = "
+ tr("optimistic unchoke");
}
// S = Peer is snubbed
if (isSnubbed()) {
m_flags += "S ";
m_flagsDescription += tr("peer snubbed");
m_flagsDescription += ", ";
flagsDescriptionList += "S = "
+ tr("peer snubbed");
}
// I = Peer is an incoming connection
if (!isLocalConnection()) {
m_flags += "I ";
m_flagsDescription += tr("incoming connection");
m_flagsDescription += ", ";
flagsDescriptionList += "I = "
+ tr("incoming connection");
}
// K = Peer is unchoking your client, but your client is not interested
if (!isRemoteChocked() && !isInteresting()) {
m_flags += "K ";
m_flagsDescription += tr("not interested(local) and unchoked(peer)");
m_flagsDescription += ", ";
flagsDescriptionList += "K = "
+ tr("not interested(local) and unchoked(peer)");
}
// ? = Your client unchoked the peer but the peer is not interested
if (!isChocked() && !isRemoteInterested()) {
m_flags += "? ";
m_flagsDescription += tr("not interested(peer) and unchoked(local)");
m_flagsDescription += ", ";
flagsDescriptionList += "? = "
+ tr("not interested(peer) and unchoked(local)");
}
// X = Peer was included in peerlists obtained through Peer Exchange (PEX)
if (fromPeX()) {
m_flags += "X ";
m_flagsDescription += tr("peer from PEX");
m_flagsDescription += ", ";
flagsDescriptionList += "X = "
+ tr("peer from PEX");
}
// H = Peer was obtained through DHT
if (fromDHT()) {
m_flags += "H ";
m_flagsDescription += tr("peer from DHT");
m_flagsDescription += ", ";
flagsDescriptionList += "H = "
+ tr("peer from DHT");
}
// E = Peer is using Protocol Encryption (all traffic)
if (isRC4Encrypted()) {
m_flags += "E ";
m_flagsDescription += tr("encrypted traffic");
m_flagsDescription += ", ";
flagsDescriptionList += "E = "
+ tr("encrypted traffic");
}
// e = Peer is using Protocol Encryption (handshake)
if (isPlaintextEncrypted()) {
m_flags += "e ";
m_flagsDescription += tr("encrypted handshake");
m_flagsDescription += ", ";
flagsDescriptionList += "e = "
+ tr("encrypted handshake");
}
// P = Peer is using uTorrent uTP
if (useUTPSocket()) {
m_flags += "P ";
m_flagsDescription += QString::fromUtf8(C_UTP);
m_flagsDescription += ", ";
flagsDescriptionList += "P = "
+ QString::fromUtf8(C_UTP);
}
// L = Peer is local
if (fromLSD()) {
m_flags += "L";
m_flagsDescription += tr("peer from LSD");
flagsDescriptionList += "L = "
+ tr("peer from LSD");
}
m_flags = m_flags.trimmed();
m_flagsDescription = m_flagsDescription.trimmed();
if (m_flagsDescription.endsWith(',', Qt::CaseInsensitive))
m_flagsDescription.chop(1);
m_flagsDescription = flagsDescriptionList.join('\n');
}
QString PeerInfo::flags() const

View File

@@ -102,6 +102,7 @@ namespace
}
const int BUFFER_SIZE = 2 * 1024 * 1024; // 2 MiB
const int MAX_LOGGED_ERRORS = 5;
}
FilterParserThread::FilterParserThread(QObject *parent)
@@ -134,6 +135,12 @@ int FilterParserThread::parseDATFilterFile()
int start = 0;
int endOfLine = -1;
int nbLine = 0;
int parseErrorCount = 0;
const auto addLog = [&parseErrorCount](const QString &msg)
{
if (parseErrorCount <= MAX_LOGGED_ERRORS)
LogMsg(msg, Log::CRITICAL);
};
while (true) {
bytesRead = file.read(buffer.data() + offset, BUFFER_SIZE - offset - 1);
@@ -202,7 +209,8 @@ int FilterParserThread::parseDATFilterFile()
int endOfIPRange = ((firstComma == -1) ? (endOfLine - 1) : (firstComma - 1));
int delimIP = findAndNullDelimiter(buffer.data(), '-', start, endOfIPRange);
if (delimIP == -1) {
LogMsg(tr("IP filter line %1 is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
@@ -210,7 +218,8 @@ int FilterParserThread::parseDATFilterFile()
libt::address startAddr;
int newStart = trim(buffer.data(), start, delimIP - 1);
if (!parseIPAddress(buffer.data() + newStart, startAddr)) {
LogMsg(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
@@ -218,14 +227,16 @@ int FilterParserThread::parseDATFilterFile()
libt::address endAddr;
newStart = trim(buffer.data(), delimIP + 1, endOfIPRange);
if (!parseIPAddress(buffer.data() + newStart, endAddr)) {
LogMsg(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
if ((startAddr.is_v4() != endAddr.is_v4())
|| (startAddr.is_v6() != endAddr.is_v6())) {
LogMsg(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine));
start = endOfLine;
continue;
}
@@ -238,8 +249,9 @@ int FilterParserThread::parseDATFilterFile()
++ruleCount;
}
catch (std::exception &e) {
LogMsg(tr("IP filter exception thrown for line %1. Exception is: %2").arg(nbLine)
.arg(QString::fromLocal8Bit(e.what())), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter exception thrown for line %1. Exception is: %2")
.arg(nbLine).arg(QString::fromLocal8Bit(e.what())));
}
}
@@ -247,6 +259,9 @@ int FilterParserThread::parseDATFilterFile()
offset = 0;
}
if (parseErrorCount > MAX_LOGGED_ERRORS)
LogMsg(tr("%1 extra IP filter parsing errors occurred.", "513 extra IP filter parsing errors occurred.")
.arg(parseErrorCount - MAX_LOGGED_ERRORS), Log::CRITICAL);
return ruleCount;
}
@@ -268,6 +283,12 @@ int FilterParserThread::parseP2PFilterFile()
int start = 0;
int endOfLine = -1;
int nbLine = 0;
int parseErrorCount = 0;
const auto addLog = [&parseErrorCount](const QString &msg)
{
if (parseErrorCount <= MAX_LOGGED_ERRORS)
LogMsg(msg, Log::CRITICAL);
};
while (true) {
bytesRead = file.read(buffer.data() + offset, BUFFER_SIZE - offset - 1);
@@ -319,7 +340,8 @@ int FilterParserThread::parseP2PFilterFile()
// The "Some organization" part might contain a ':' char itself so we find the last occurrence
int partsDelimiter = findAndNullDelimiter(buffer.data(), ':', start, endOfLine, true);
if (partsDelimiter == -1) {
LogMsg(tr("IP filter line %1 is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
@@ -327,7 +349,8 @@ int FilterParserThread::parseP2PFilterFile()
// IP Range should be split by a dash
int delimIP = findAndNullDelimiter(buffer.data(), '-', partsDelimiter + 1, endOfLine);
if (delimIP == -1) {
LogMsg(tr("IP filter line %1 is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
@@ -335,7 +358,8 @@ int FilterParserThread::parseP2PFilterFile()
libt::address startAddr;
int newStart = trim(buffer.data(), partsDelimiter + 1, delimIP - 1);
if (!parseIPAddress(buffer.data() + newStart, startAddr)) {
LogMsg(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. Start IP of the range is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
@@ -343,14 +367,16 @@ int FilterParserThread::parseP2PFilterFile()
libt::address endAddr;
newStart = trim(buffer.data(), delimIP + 1, endOfLine);
if (!parseIPAddress(buffer.data() + newStart, endAddr)) {
LogMsg(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. End IP of the range is malformed.").arg(nbLine));
start = endOfLine;
continue;
}
if ((startAddr.is_v4() != endAddr.is_v4())
|| (startAddr.is_v6() != endAddr.is_v6())) {
LogMsg(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter line %1 is malformed. One IP is IPv4 and the other is IPv6!").arg(nbLine));
start = endOfLine;
continue;
}
@@ -362,8 +388,9 @@ int FilterParserThread::parseP2PFilterFile()
++ruleCount;
}
catch (std::exception &e) {
LogMsg(tr("IP filter exception thrown for line %1. Exception is: %2").arg(nbLine)
.arg(QString::fromLocal8Bit(e.what())), Log::CRITICAL);
++parseErrorCount;
addLog(tr("IP filter exception thrown for line %1. Exception is: %2")
.arg(nbLine).arg(QString::fromLocal8Bit(e.what())));
}
}
@@ -371,6 +398,9 @@ int FilterParserThread::parseP2PFilterFile()
offset = 0;
}
if (parseErrorCount > MAX_LOGGED_ERRORS)
LogMsg(tr("%1 extra IP filter parsing errors occurred.", "513 extra IP filter parsing errors occurred.")
.arg(parseErrorCount - MAX_LOGGED_ERRORS), Log::CRITICAL);
return ruleCount;
}

View File

@@ -304,7 +304,7 @@ Session::Session(QObject *parent)
, m_btProtocol(BITTORRENT_SESSION_KEY("BTProtocol"), BTProtocol::Both
, clampValue(BTProtocol::Both, BTProtocol::UTP))
, m_isUTPRateLimited(BITTORRENT_SESSION_KEY("uTPRateLimited"), true)
, m_utpMixedMode(BITTORRENT_SESSION_KEY("uTPMixedMode"), MixedModeAlgorithm::Proportional
, m_utpMixedMode(BITTORRENT_SESSION_KEY("uTPMixedMode"), MixedModeAlgorithm::TCP
, clampValue(MixedModeAlgorithm::TCP, MixedModeAlgorithm::Proportional))
, m_multiConnectionsPerIpEnabled(BITTORRENT_SESSION_KEY("MultiConnectionsPerIp"), false)
, m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false)
@@ -1418,8 +1418,11 @@ void Session::configure(libtorrent::settings_pack &settingsPack)
void Session::configurePeerClasses()
{
libt::ip_filter f;
f.add_rule(libt::address_v4::from_string("0.0.0.0")
, libt::address_v4::from_string("255.255.255.255")
// address_v4::from_string("255.255.255.255") crashes on some people's systems
// so instead we use address_v4::broadcast()
// Proactively do the same for 0.0.0.0 and address_v4::any()
f.add_rule(libt::address_v4::any()
, libt::address_v4::broadcast()
, 1 << libt::session::global_peer_class_id);
#if TORRENT_USE_IPV6
// IPv6 may not be available on OS and the parsing
@@ -1478,14 +1481,10 @@ void Session::configurePeerClasses()
peerClassTypeFilter.add(libt::peer_class_type_filter::tcp_socket, libt::session::tcp_peer_class_id);
peerClassTypeFilter.add(libt::peer_class_type_filter::ssl_tcp_socket, libt::session::tcp_peer_class_id);
peerClassTypeFilter.add(libt::peer_class_type_filter::i2p_socket, libt::session::tcp_peer_class_id);
if (isUTPRateLimited()) {
peerClassTypeFilter.add(libt::peer_class_type_filter::utp_socket
, libt::session::local_peer_class_id);
peerClassTypeFilter.add(libt::peer_class_type_filter::utp_socket
if (!isUTPRateLimited()) {
peerClassTypeFilter.disallow(libt::peer_class_type_filter::utp_socket
, libt::session::global_peer_class_id);
peerClassTypeFilter.add(libt::peer_class_type_filter::ssl_utp_socket
, libt::session::local_peer_class_id);
peerClassTypeFilter.add(libt::peer_class_type_filter::ssl_utp_socket
peerClassTypeFilter.disallow(libt::peer_class_type_filter::ssl_utp_socket
, libt::session::global_peer_class_id);
}
m_nativeSession->set_peer_class_type_filter(peerClassTypeFilter);

View File

@@ -59,8 +59,6 @@ using namespace BitTorrent;
TorrentCreatorThread::TorrentCreatorThread(QObject *parent)
: QThread(parent)
, m_private(false)
, m_pieceSize(0)
{
}
@@ -70,97 +68,91 @@ TorrentCreatorThread::~TorrentCreatorThread()
wait();
}
void TorrentCreatorThread::create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize)
void TorrentCreatorThread::create(const TorrentCreatorParams &params)
{
m_inputPath = Utils::Fs::fromNativePath(inputPath);
m_savePath = Utils::Fs::fromNativePath(savePath);
if (QFile(m_savePath).exists())
Utils::Fs::forceRemove(m_savePath);
m_trackers = trackers;
m_urlSeeds = urlSeeds;
m_comment = comment;
m_private = isPrivate;
m_pieceSize = pieceSize;
m_params = params;
start();
}
void TorrentCreatorThread::sendProgressSignal(int numHashes, int numPieces)
void TorrentCreatorThread::sendProgressSignal(int currentPieceIdx, int totalPieces)
{
emit updateProgress(static_cast<int>((numHashes * 100.) / numPieces));
emit updateProgress(static_cast<int>((currentPieceIdx * 100.) / totalPieces));
}
void TorrentCreatorThread::run()
{
const QString creatorStr("qBittorrent " QBT_VERSION);
emit updateProgress(0);
QString creator_str("qBittorrent " QBT_VERSION);
try {
libt::file_storage fs;
// Adding files to the torrent
libt::add_files(fs, Utils::Fs::toNativePath(m_inputPath).toStdString(), fileFilter);
libt::add_files(fs, Utils::Fs::toNativePath(m_params.inputPath).toStdString(), fileFilter);
if (isInterruptionRequested()) return;
libt::create_torrent t(fs, m_pieceSize);
libt::create_torrent newTorrent(fs, m_params.pieceSize);
// Add url seeds
foreach (const QString &seed, m_urlSeeds)
t.add_url_seed(seed.trimmed().toStdString());
foreach (QString seed, m_params.urlSeeds) {
seed = seed.trimmed();
if (!seed.isEmpty())
newTorrent.add_url_seed(seed.toStdString());
}
int tier = 0;
bool newline = false;
foreach (const QString &tracker, m_trackers) {
if (tracker.isEmpty()) {
if (newline)
continue;
foreach (const QString &tracker, m_params.trackers) {
if (tracker.isEmpty())
++tier;
newline = true;
continue;
}
t.add_tracker(tracker.trimmed().toStdString(), tier);
newline = false;
else
newTorrent.add_tracker(tracker.trimmed().toStdString(), tier);
}
if (isInterruptionRequested()) return;
// calculate the hash for all pieces
const QString parentPath = Utils::Fs::branchPath(m_inputPath) + "/";
libt::set_piece_hashes(t, Utils::Fs::toNativePath(parentPath).toStdString(), boost::bind(&TorrentCreatorThread::sendProgressSignal, this, _1, t.num_pieces()));
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + "/";
libt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
, [this, &newTorrent](const int n) { sendProgressSignal(n, newTorrent.num_pieces()); });
// Set qBittorrent as creator and add user comment to
// torrent_info structure
t.set_creator(creator_str.toUtf8().constData());
t.set_comment(m_comment.toUtf8().constData());
newTorrent.set_creator(creatorStr.toUtf8().constData());
newTorrent.set_comment(m_params.comment.toUtf8().constData());
// Is private ?
t.set_priv(m_private);
newTorrent.set_priv(m_params.isPrivate);
if (isInterruptionRequested()) return;
// create the torrent and print it to out
qDebug("Saving to %s", qUtf8Printable(m_savePath));
libt::entry entry = newTorrent.generate();
// add source field
if (!m_params.source.isEmpty())
entry["info"]["source"] = m_params.source.toStdString();
if (isInterruptionRequested()) return;
// create the torrent
std::ofstream outfile(
#ifdef _MSC_VER
wchar_t *savePathW = new wchar_t[m_savePath.length() + 1];
int len = Utils::Fs::toNativePath(m_savePath).toWCharArray(savePathW);
savePathW[len] = L'\0';
std::ofstream outfile(savePathW, std::ios_base::out | std::ios_base::binary);
delete[] savePathW;
Utils::Fs::toNativePath(m_params.savePath).toStdWString().c_str()
#else
std::ofstream outfile(Utils::Fs::toNativePath(m_savePath).toLocal8Bit().constData(), std::ios_base::out | std::ios_base::binary);
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::exception();
throw std::runtime_error(tr("create new torrent file failed").toStdString());
if (isInterruptionRequested()) return;
libt::bencode(std::ostream_iterator<char>(outfile), t.generate());
libt::bencode(std::ostream_iterator<char>(outfile), entry);
outfile.close();
emit updateProgress(100);
emit creationSuccess(m_savePath, parentPath);
emit creationSuccess(m_params.savePath, parentPath);
}
catch (std::exception& e) {
emit creationFailure(QString::fromStdString(e.what()));
catch (const std::exception &e) {
emit creationFailure(e.what());
}
}

View File

@@ -36,16 +36,27 @@
namespace BitTorrent
{
struct TorrentCreatorParams
{
bool isPrivate;
int pieceSize;
QString inputPath;
QString savePath;
QString comment;
QString source;
QStringList trackers;
QStringList urlSeeds;
};
class TorrentCreatorThread : public QThread
{
Q_OBJECT
public:
TorrentCreatorThread(QObject *parent = 0);
TorrentCreatorThread(QObject *parent = nullptr);
~TorrentCreatorThread();
void create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize);
void create(const TorrentCreatorParams &params);
static int calculateTotalPieces(const QString &inputPath, const int pieceSize);
@@ -58,15 +69,9 @@ namespace BitTorrent
void updateProgress(int progress);
private:
void sendProgressSignal(int numHashes, int numPieces);
void sendProgressSignal(int currentPieceIdx, int totalPieces);
QString m_inputPath;
QString m_savePath;
QStringList m_trackers;
QStringList m_urlSeeds;
QString m_comment;
bool m_private;
int m_pieceSize;
TorrentCreatorParams m_params;
};
}

View File

@@ -156,6 +156,8 @@ const int TorrentHandle::MAX_SEEDING_TIME = 525600;
// The following can be removed after one or two libtorrent releases on each branch.
namespace
{
const char i18nContext[] = "TorrentHandle";
// new constructor is available
template<typename T, typename std::enable_if<std::is_constructible<T, libt::torrent_info, bool>::value, int>::type = 0>
T makeTorrentCreator(const libtorrent::torrent_info & ti)
@@ -1469,7 +1471,7 @@ void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_fail
return;
}
Logger::instance()->addMessage(tr("Could not move torrent: '%1'. Reason: %2")
LogMsg(QCoreApplication::translate(i18nContext, "Could not move torrent: '%1'. Reason: %2")
.arg(name()).arg(QString::fromStdString(p->message())), Log::CRITICAL);
m_moveStorageInfo.newPath.clear();
@@ -1647,13 +1649,13 @@ void TorrentHandle::handleFastResumeRejectedAlert(libtorrent::fastresume_rejecte
updateStatus();
if (p->error.value() == libt::errors::mismatching_file_size) {
// Mismatching file size (files were probably moved)
logger->addMessage(tr("File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
logger->addMessage(QCoreApplication::translate(i18nContext, "File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
m_hasMissingFiles = true;
if (!isPaused())
pause();
}
else {
logger->addMessage(tr("Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
logger->addMessage(QCoreApplication::translate(i18nContext, "Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
.arg(name()).arg(QString::fromStdString(p->message())), Log::CRITICAL);
}
}

View File

@@ -59,6 +59,7 @@ namespace Http
const char CONTENT_TYPE_JSON[] = "application/json";
const char CONTENT_TYPE_PNG[] = "image/png";
const char CONTENT_TYPE_TXT[] = "text/plain; charset=UTF-8";
const char CONTENT_TYPE_SVG[] = "image/svg+xml";
// portability: "\r\n" doesn't guarantee mapping to the correct value
const char CRLF[] = {0x0D, 0x0A, '\0'};

View File

@@ -35,6 +35,7 @@
#include <QCryptographicHash>
#include <QDir>
#include <QLocale>
#include <QMutableListIterator>
#include <QPair>
#include <QSettings>
@@ -487,13 +488,17 @@ QList<Utils::Net::Subnet> Preferences::getWebUiAuthSubnetWhitelist() const
return subnets;
}
void Preferences::setWebUiAuthSubnetWhitelist(const QList<Utils::Net::Subnet> &subnets)
void Preferences::setWebUiAuthSubnetWhitelist(QStringList subnets)
{
QStringList subnetsStringList;
for (const Utils::Net::Subnet &subnet : subnets)
subnetsStringList.append(Utils::Net::subnetToString(subnet));
QMutableListIterator<QString> i(subnets);
while (i.hasNext()) {
bool ok = false;
const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(i.next().trimmed(), &ok);
if (!ok)
i.remove();
}
setValue("Preferences/WebUI/AuthSubnetWhitelist", subnetsStringList);
setValue("Preferences/WebUI/AuthSubnetWhitelist", subnets);
}
QString Preferences::getServerDomains() const
@@ -1216,9 +1221,9 @@ void Preferences::setMainLastDir(const QString &path)
setValue("MainWindowLastDir", path);
}
QSize Preferences::getPrefSize(const QSize &defaultSize) const
QSize Preferences::getPrefSize() const
{
return value("Preferences/State/size", defaultSize).toSize();
return value("Preferences/State/size").toSize();
}
void Preferences::setPrefSize(const QSize &size)
@@ -1296,9 +1301,9 @@ void Preferences::setPropTrackerListState(const QByteArray &state)
setValue("TorrentProperties/Trackers/qt5/TrackerListState", state);
}
QSize Preferences::getRssGeometrySize(const QSize &defaultSize) const
QSize Preferences::getRssGeometrySize() const
{
return value("RssFeedDownloader/geometrySize", defaultSize).toSize();
return value("RssFeedDownloader/geometrySize").toSize();
}
void Preferences::setRssGeometrySize(const QSize &geometry)

View File

@@ -191,7 +191,7 @@ public:
bool isWebUiAuthSubnetWhitelistEnabled() const;
void setWebUiAuthSubnetWhitelistEnabled(bool enabled);
QList<Utils::Net::Subnet> getWebUiAuthSubnetWhitelist() const;
void setWebUiAuthSubnetWhitelist(const QList<Utils::Net::Subnet> &subnets);
void setWebUiAuthSubnetWhitelist(QStringList subnets);
QString getWebUiUsername() const;
void setWebUiUsername(const QString &username);
QString getWebUiPassword() const;
@@ -301,7 +301,7 @@ public:
void setMainVSplitterState(const QByteArray &state);
QString getMainLastDir() const;
void setMainLastDir(const QString &path);
QSize getPrefSize(const QSize &defaultSize) const;
QSize getPrefSize() const;
void setPrefSize(const QSize &size);
QStringList getPrefHSplitterSizes() const;
void setPrefHSplitterSizes(const QStringList &sizes);
@@ -317,7 +317,7 @@ public:
void setPropVisible(const bool visible);
QByteArray getPropTrackerListState() const;
void setPropTrackerListState(const QByteArray &state);
QSize getRssGeometrySize(const QSize &defaultSize) const;
QSize getRssGeometrySize() const;
void setRssGeometrySize(const QSize &geometry);
QByteArray getRssHSplitterSizes() const;
void setRssHSplitterSizes(const QByteArray &sizes);

View File

@@ -227,10 +227,9 @@ void Parser::parse(const QByteArray &feedData)
// read and create items from a rss document
void Parser::parse_impl(const QByteArray &feedData)
{
qDebug() << Q_FUNC_INFO;
QXmlStreamReader xml(feedData);
bool foundChannel = false;
while (xml.readNextStartElement()) {
if (xml.name() == "rss") {
// Find channels
@@ -258,11 +257,15 @@ void Parser::parse_impl(const QByteArray &feedData)
}
}
if (xml.hasError())
m_result.error = xml.errorString();
else if (!foundChannel)
if (!foundChannel) {
m_result.error = tr("Invalid RSS feed.");
else
}
else if (xml.hasError()) {
m_result.error = tr("%1 (line: %2, column: %3, offset: %4).")
.arg(xml.errorString()).arg(xml.lineNumber())
.arg(xml.columnNumber()).arg(xml.characterOffset());
}
else {
// Sort article list chronologically
// NOTE: We don't need to sort it here if articles are always
// sorted in fetched XML in reverse chronological order
@@ -271,6 +274,7 @@ void Parser::parse_impl(const QByteArray &feedData)
{
return a1["date"].toDateTime() < a2["date"].toDateTime();
});
}
emit finished(m_result);
m_result.articles.clear(); // clear articles only
@@ -288,35 +292,34 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
break;
if (xml.isStartElement()) {
const QString text(xml.readElementText().trimmed());
if (name == QLatin1String("title")) {
article[Article::KeyTitle] = text;
article[Article::KeyTitle] = xml.readElementText().trimmed();
}
else if (name == QLatin1String("enclosure")) {
if (xml.attributes().value("type") == QLatin1String("application/x-bittorrent"))
article[Article::KeyTorrentURL] = xml.attributes().value(QLatin1String("url")).toString();
}
else if (name == QLatin1String("link")) {
const QString text {xml.readElementText().trimmed()};
if (text.startsWith(QLatin1String("magnet:"), Qt::CaseInsensitive))
article[Article::KeyTorrentURL] = text; // magnet link instead of a news URL
else
article[Article::KeyLink] = text;
}
else if (name == QLatin1String("description")) {
article[Article::KeyDescription] = text;
article[Article::KeyDescription] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
}
else if (name == QLatin1String("pubDate")) {
article[Article::KeyDate] = parseDate(text);
article[Article::KeyDate] = parseDate(xml.readElementText().trimmed());
}
else if (name == QLatin1String("author")) {
article[Article::KeyAuthor] = text;
article[Article::KeyAuthor] = xml.readElementText().trimmed();
}
else if (name == QLatin1String("guid")) {
article[Article::KeyId] = text;
article[Article::KeyId] = xml.readElementText().trimmed();
}
else {
article[name] = text;
article[name] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
}
}
}
@@ -326,17 +329,14 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
void Parser::parseRSSChannel(QXmlStreamReader &xml)
{
qDebug() << Q_FUNC_INFO;
Q_ASSERT(xml.isStartElement() && xml.name() == "channel");
while (!xml.atEnd()) {
xml.readNext();
if (xml.isStartElement()) {
if (xml.name() == "title") {
if (xml.name() == QLatin1String("title")) {
m_result.title = xml.readElementText();
}
else if (xml.name() == "lastBuildDate") {
else if (xml.name() == QLatin1String("lastBuildDate")) {
QString lastBuildDate = xml.readElementText();
if (!lastBuildDate.isEmpty()) {
if (m_result.lastBuildDate == lastBuildDate) {
@@ -346,7 +346,7 @@ void Parser::parseRSSChannel(QXmlStreamReader &xml)
m_result.lastBuildDate = lastBuildDate;
}
}
else if (xml.name() == "item") {
else if (xml.name() == QLatin1String("item")) {
parseRssArticle(xml);
}
}
@@ -366,14 +366,12 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
break;
if (xml.isStartElement()) {
const QString text(xml.readElementText().trimmed());
if (name == QLatin1String("title")) {
article[Article::KeyTitle] = text;
article[Article::KeyTitle] = xml.readElementText().trimmed();
}
else if (name == QLatin1String("link")) {
QString link = (xml.attributes().isEmpty()
? text
? xml.readElementText().trimmed()
: xml.attributes().value(QLatin1String("href")).toString());
if (link.startsWith(QLatin1String("magnet:"), Qt::CaseInsensitive))
@@ -385,42 +383,38 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
article[Article::KeyLink] = (m_baseUrl.isEmpty() ? link : m_baseUrl + link);
}
else if ((name == QLatin1String("summary")) || (name == QLatin1String("content"))){
else if ((name == QLatin1String("summary")) || (name == QLatin1String("content"))) {
if (doubleContent) { // Duplicate content -> ignore
xml.readNext();
while ((xml.name() != QLatin1String("summary")) && (xml.name() != QLatin1String("content")))
xml.readNext();
xml.skipCurrentElement();
continue;
}
// Try to also parse broken articles, which don't use html '&' escapes
// Actually works great for non-broken content too
QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements);
if (!feedText.isEmpty())
article[Article::KeyDescription] = feedText.trimmed();
doubleContent = true;
QString feedText = xml.readElementText(QXmlStreamReader::IncludeChildElements).trimmed();
if (!feedText.isEmpty()) {
article[Article::KeyDescription] = feedText;
doubleContent = true;
}
}
else if (name == QLatin1String("updated")) {
// ATOM uses standard compliant date, don't do fancy stuff
QDateTime articleDate = QDateTime::fromString(text, Qt::ISODate);
QDateTime articleDate = QDateTime::fromString(xml.readElementText().trimmed(), Qt::ISODate);
article[Article::KeyDate] = (articleDate.isValid() ? articleDate : QDateTime::currentDateTime());
}
else if (name == QLatin1String("author")) {
xml.readNext();
while (xml.name() != QLatin1String("author")) {
while (xml.readNextStartElement()) {
if (xml.name() == QLatin1String("name"))
article[Article::KeyAuthor] = xml.readElementText().trimmed();
xml.readNext();
else
xml.skipCurrentElement();
}
}
else if (name == QLatin1String("id")) {
article[Article::KeyId] = text;
article[Article::KeyId] = xml.readElementText().trimmed();
}
else {
article[name] = text;
article[name] = xml.readElementText(QXmlStreamReader::IncludeChildElements);
}
}
}
@@ -430,19 +424,16 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
void Parser::parseAtomChannel(QXmlStreamReader &xml)
{
qDebug() << Q_FUNC_INFO;
Q_ASSERT(xml.isStartElement() && xml.name() == "feed");
m_baseUrl = xml.attributes().value("xml:base").toString();
while (!xml.atEnd()) {
xml.readNext();
if (xml.isStartElement()) {
if (xml.name() == "title") {
if (xml.name() == QLatin1String("title")) {
m_result.title = xml.readElementText();
}
else if (xml.name() == "updated") {
else if (xml.name() == QLatin1String("updated")) {
QString lastBuildDate = xml.readElementText();
if (!lastBuildDate.isEmpty()) {
if (m_result.lastBuildDate == lastBuildDate) {
@@ -452,7 +443,7 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml)
m_result.lastBuildDate = lastBuildDate;
}
}
else if (xml.name() == "entry") {
else if (xml.name() == QLatin1String("entry")) {
parseAtomArticle(xml);
}
}

View File

@@ -28,6 +28,7 @@
#include "rss_autodownloader.h"
#include <QDataStream>
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
@@ -37,6 +38,7 @@
#include <QThread>
#include <QTimer>
#include <QVariant>
#include <QVector>
#include "../bittorrent/magneturi.h"
#include "../bittorrent/session.h"
@@ -63,6 +65,32 @@ const QString RulesFileName(QStringLiteral("download_rules.json"));
const QString SettingsKey_ProcessingEnabled(QStringLiteral("RSS/AutoDownloader/EnableProcessing"));
namespace
{
QVector<RSS::AutoDownloadRule> rulesFromJSON(const QByteArray &jsonData)
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &jsonError);
if (jsonError.error != QJsonParseError::NoError)
throw RSS::ParsingError(jsonError.errorString());
if (!jsonDoc.isObject())
throw RSS::ParsingError(RSS::AutoDownloader::tr("Invalid data format."));
const QJsonObject jsonObj {jsonDoc.object()};
QVector<RSS::AutoDownloadRule> rules;
for (auto it = jsonObj.begin(); it != jsonObj.end(); ++it) {
const QJsonValue jsonVal {it.value()};
if (!jsonVal.isObject())
throw RSS::ParsingError(RSS::AutoDownloader::tr("Invalid data format."));
rules.append(RSS::AutoDownloadRule::fromJsonObject(jsonVal.toObject(), it.key()));
}
return rules;
}
}
using namespace RSS;
QPointer<AutoDownloader> AutoDownloader::m_instance = nullptr;
@@ -84,8 +112,8 @@ AutoDownloader::AutoDownloader()
connect(m_ioThread, &QThread::finished, m_fileStorage, &AsyncFileStorage::deleteLater);
connect(m_fileStorage, &AsyncFileStorage::failed, [](const QString &fileName, const QString &errorString)
{
Logger::instance()->addMessage(QString("Couldn't save RSS AutoDownloader data in %1. Error: %2")
.arg(fileName).arg(errorString), Log::WARNING);
LogMsg(tr("Couldn't save RSS AutoDownloader data in %1. Error: %2")
.arg(fileName).arg(errorString), Log::CRITICAL);
});
m_ioThread->start();
@@ -174,6 +202,70 @@ void AutoDownloader::removeRule(const QString &ruleName)
}
}
QByteArray AutoDownloader::exportRules(AutoDownloader::RulesFileFormat format) const
{
switch (format) {
case RulesFileFormat::Legacy:
return exportRulesToLegacyFormat();
default:
return exportRulesToJSONFormat();
}
}
void AutoDownloader::importRules(const QByteArray &data, AutoDownloader::RulesFileFormat format)
{
switch (format) {
case RulesFileFormat::Legacy:
importRulesFromLegacyFormat(data);
break;
default:
importRulesFromJSONFormat(data);
}
}
QByteArray AutoDownloader::exportRulesToJSONFormat() const
{
QJsonObject jsonObj;
for (const auto &rule : rules())
jsonObj.insert(rule.name(), rule.toJsonObject());
return QJsonDocument(jsonObj).toJson();
}
void AutoDownloader::importRulesFromJSONFormat(const QByteArray &data)
{
const auto rules = rulesFromJSON(data);
for (const auto &rule : rules)
insertRule(rule);
}
QByteArray AutoDownloader::exportRulesToLegacyFormat() const
{
QVariantHash dict;
for (const auto &rule : rules())
dict[rule.name()] = rule.toLegacyDict();
QByteArray data;
QDataStream out(&data, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_5);
out << dict;
return data;
}
void AutoDownloader::importRulesFromLegacyFormat(const QByteArray &data)
{
QDataStream in(data);
in.setVersion(QDataStream::Qt_4_5);
QVariantHash dict;
in >> dict;
if (in.status() != QDataStream::Ok)
throw ParsingError(tr("Invalid data format"));
for (const QVariant &val : dict)
insertRule(AutoDownloadRule::fromLegacyDict(val.toHash()));
}
void AutoDownloader::process()
{
if (m_processingQueue.isEmpty()) return; // processing was disabled
@@ -276,39 +368,20 @@ void AutoDownloader::load()
else if (rulesFile.open(QFile::ReadOnly))
loadRules(rulesFile.readAll());
else
Logger::instance()->addMessage(
QString("Couldn't read RSS AutoDownloader rules from %1. Error: %2")
.arg(rulesFile.fileName()).arg(rulesFile.errorString()), Log::WARNING);
LogMsg(tr("Couldn't read RSS AutoDownloader rules from %1. Error: %2")
.arg(rulesFile.fileName()).arg(rulesFile.errorString()), Log::CRITICAL);
}
void AutoDownloader::loadRules(const QByteArray &data)
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
if (jsonError.error != QJsonParseError::NoError) {
Logger::instance()->addMessage(
QString("Couldn't parse RSS AutoDownloader rules. Error: %1")
.arg(jsonError.errorString()), Log::WARNING);
return;
try {
const auto rules = rulesFromJSON(data);
for (const auto &rule : rules)
setRule_impl(rule);
}
if (!jsonDoc.isObject()) {
Logger::instance()->addMessage(
QString("Couldn't load RSS AutoDownloader rules. Invalid data format."), Log::WARNING);
return;
}
QJsonObject jsonObj = jsonDoc.object();
foreach (const QString &key, jsonObj.keys()) {
const QJsonValue jsonVal = jsonObj.value(key);
if (!jsonVal.isObject()) {
Logger::instance()->addMessage(
QString("Couldn't load RSS AutoDownloader rule '%1'. Invalid data format.")
.arg(key), Log::WARNING);
continue;
}
setRule_impl(AutoDownloadRule::fromJsonObject(jsonVal.toObject(), key));
catch (const ParsingError &error) {
LogMsg(tr("Couldn't load RSS AutoDownloader rules. Reason: %1")
.arg(error.message()), Log::CRITICAL);
}
}
@@ -317,7 +390,7 @@ void AutoDownloader::loadRulesLegacy()
SettingsPtr settings = Profile::instance().applicationSettings(QStringLiteral("qBittorrent-rss"));
QVariantHash rules = settings->value(QStringLiteral("download_rules")).toHash();
foreach (const QVariant &ruleVar, rules) {
auto rule = AutoDownloadRule::fromVariantHash(ruleVar.toHash());
auto rule = AutoDownloadRule::fromLegacyDict(ruleVar.toHash());
if (!rule.name().isEmpty())
insertRule(rule);
}
@@ -351,6 +424,8 @@ bool AutoDownloader::isProcessingEnabled() const
void AutoDownloader::resetProcessingQueue()
{
m_processingQueue.clear();
if (!m_processingEnabled) return;
foreach (Article *article, Session::instance()->rootFolder()->articles()) {
if (!article->isRead() && !article->torrentUrl().isEmpty())
addJobForArticle(article);
@@ -385,3 +460,13 @@ void AutoDownloader::timerEvent(QTimerEvent *event)
Q_UNUSED(event);
store();
}
ParsingError::ParsingError(const QString &message)
: std::runtime_error(message.toUtf8().data())
{
}
QString ParsingError::message() const
{
return what();
}

View File

@@ -28,6 +28,8 @@
#pragma once
#include <stdexcept>
#include <QBasicTimer>
#include <QHash>
#include <QList>
@@ -49,6 +51,13 @@ namespace RSS
class AutoDownloadRule;
class ParsingError : public std::runtime_error
{
public:
explicit ParsingError(const QString &message);
QString message() const;
};
class AutoDownloader final: public QObject
{
Q_OBJECT
@@ -60,6 +69,12 @@ namespace RSS
~AutoDownloader() override;
public:
enum class RulesFileFormat
{
Legacy,
JSON
};
static AutoDownloader *instance();
bool isProcessingEnabled() const;
@@ -73,6 +88,9 @@ namespace RSS
bool renameRule(const QString &ruleName, const QString &newRuleName);
void removeRule(const QString &ruleName);
QByteArray exportRules(RulesFileFormat format = RulesFileFormat::JSON) const;
void importRules(const QByteArray &data, RulesFileFormat format = RulesFileFormat::JSON);
signals:
void processingStateChanged(bool enabled);
void ruleAdded(const QString &ruleName);
@@ -98,6 +116,10 @@ namespace RSS
void loadRulesLegacy();
void store();
void storeDeferred();
QByteArray exportRulesToJSONFormat() const;
void importRulesFromJSONFormat(const QByteArray &data);
QByteArray exportRulesToLegacyFormat() const;
void importRulesFromLegacyFormat(const QByteArray &data);
static QPointer<AutoDownloader> m_instance;

View File

@@ -63,11 +63,29 @@ namespace
QJsonValue triStateBoolToJsonValue(const TriStateBool &triStateBool)
{
switch (static_cast<int>(triStateBool)) {
case 0: return false; break;
case 1: return true; break;
case 0: return false;
case 1: return true;
default: return QJsonValue();
}
}
TriStateBool addPausedLegacyToTriStateBool(int val)
{
switch (val) {
case 1: return TriStateBool::True; // always
case 2: return TriStateBool::False; // never
default: return TriStateBool::Undefined; // default
}
}
int triStateBoolToAddPausedLegacy(const TriStateBool &triStateBool)
{
switch (static_cast<int>(triStateBool)) {
case 0: return 2; // never
case 1: return 1; // always
default: return 0; // default
}
}
}
const QString Str_Name(QStringLiteral("name"));
@@ -378,21 +396,37 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
return rule;
}
AutoDownloadRule AutoDownloadRule::fromVariantHash(const QVariantHash &varHash)
QVariantHash AutoDownloadRule::toLegacyDict() const
{
AutoDownloadRule rule(varHash.value("name").toString());
return {{"name", name()},
{"must_contain", mustContain()},
{"must_not_contain", mustNotContain()},
{"save_path", savePath()},
{"affected_feeds", feedURLs()},
{"enabled", isEnabled()},
{"category_assigned", assignedCategory()},
{"use_regex", useRegex()},
{"add_paused", triStateBoolToAddPausedLegacy(addPaused())},
{"episode_filter", episodeFilter()},
{"last_match", lastMatch()},
{"ignore_days", ignoreDays()}};
}
rule.setUseRegex(varHash.value("use_regex", false).toBool());
rule.setMustContain(varHash.value("must_contain").toString());
rule.setMustNotContain(varHash.value("must_not_contain").toString());
rule.setEpisodeFilter(varHash.value("episode_filter").toString());
rule.setFeedURLs(varHash.value("affected_feeds").toStringList());
rule.setEnabled(varHash.value("enabled", false).toBool());
rule.setSavePath(varHash.value("save_path").toString());
rule.setCategory(varHash.value("category_assigned").toString());
rule.setAddPaused(TriStateBool(varHash.value("add_paused").toInt() - 1));
rule.setLastMatch(varHash.value("last_match").toDateTime());
rule.setIgnoreDays(varHash.value("ignore_days").toInt());
AutoDownloadRule AutoDownloadRule::fromLegacyDict(const QVariantHash &dict)
{
AutoDownloadRule rule(dict.value("name").toString());
rule.setUseRegex(dict.value("use_regex", false).toBool());
rule.setMustContain(dict.value("must_contain").toString());
rule.setMustNotContain(dict.value("must_not_contain").toString());
rule.setEpisodeFilter(dict.value("episode_filter").toString());
rule.setFeedURLs(dict.value("affected_feeds").toStringList());
rule.setEnabled(dict.value("enabled", false).toBool());
rule.setSavePath(dict.value("save_path").toString());
rule.setCategory(dict.value("category_assigned").toString());
rule.setAddPaused(addPausedLegacyToTriStateBool(dict.value("add_paused").toInt()));
rule.setLastMatch(dict.value("last_match").toDateTime());
rule.setIgnoreDays(dict.value("ignore_days").toInt());
return rule;
}

View File

@@ -84,7 +84,9 @@ namespace RSS
QJsonObject toJsonObject() const;
static AutoDownloadRule fromJsonObject(const QJsonObject &jsonObj, const QString &name = "");
static AutoDownloadRule fromVariantHash(const QVariantHash &varHash);
QVariantHash toLegacyDict() const;
static AutoDownloadRule fromLegacyDict(const QVariantHash &dict);
private:
bool matches(const QString &articleTitle, const QString &expression) const;

View File

@@ -31,6 +31,7 @@
#include "rss_feed.h"
#include <QCryptographicHash>
#include <QDebug>
#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>

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