Compare commits

..

221 Commits

Author SHA1 Message Date
sledgehammer999
40f2718265 Sync translations from Transifex and run lupdate 2021-10-31 12:29:34 +02:00
sledgehammer999
ddd106655d Merge pull request #15622 from sledgehammer999/revert_nsis_bengali
Revert "NSIS: Add Bengali translation"
2021-10-31 12:07:49 +02:00
Vladimir Golovnev
cc61ad01b6 Explicitly set "added time" when adding new torrent
Don't overwrite "added time" with possibly incorrect value.
Explicitly set "added time" when adding new torrent.

PR #15644.
2021-10-31 12:08:19 +03:00
sledgehammer999
8a44c1f6d5 Bump to 4.4.0rc1 2021-10-31 02:56:25 +03:00
sledgehammer999
e370cbb06b Sync translations from Transifex and run lupdate 2021-10-31 02:52:52 +03:00
Likol Tung
cb0c2e3b9c Fix WebUI reverse proxy section doesn't work
As title. because load with incorrect key.
Also moving proxy list label to left, screenshot: https://imgur.com/Rz038uW
PR #15634.
2021-10-29 11:50:09 +08:00
sledgehammer999
80360cc79a Revert "NSIS: Add Bengali translation"
This reverts commit 8085db6ba9.
2021-10-25 00:46:34 +03:00
Chocobo1
559a979536 GHA CI: Add Qt6 builds
PR #15610.
2021-10-24 00:57:50 +08:00
Chocobo1
5e88537809 Merge pull request #15611 from Chocobo1/webui
Suppress WebUI CSS warnings
2021-10-24 00:57:20 +08:00
Chocobo1
8b60baea99 Remove unused CSS properties 2021-10-22 14:45:37 +08:00
Chocobo1
ac61c33d1c Use modern color notation in CSS 2021-10-22 14:43:13 +08:00
Chocobo1
3088f04e6f Use standard CSS properties instead of vendor specifics 2021-10-22 14:43:13 +08:00
Chocobo1
864dca1b67 Use percentage notation for alpha-values in CSS 2021-10-22 14:43:12 +08:00
Chocobo1
15320018f0 Use shorthand for CSS properties 2021-10-22 14:43:05 +08:00
Chocobo1
6226dd5b80 Add quotes to URL in CSS files
Better add quotes: https://stackoverflow.com/a/34383157
2021-10-22 14:43:05 +08:00
Chocobo1
aafc1064d9 Disable CSS selector naming rule 2021-10-22 14:42:12 +08:00
xavier2k6
fb2fbc875d GHA CI: Use macOS-latest VM image
PR #15589.
2021-10-22 12:16:11 +08:00
Nowshed H. Imran
8085db6ba9 NSIS: Add Bengali translation
PR #15602.
2021-10-22 05:52:11 +03:00
Vladimir Golovnev
4f20769a6c Use respective subcategory for "watched" torrents
Assign respective Subcategory of configured Category to torrents
if Automatic Torrent Management Mode is set for Watched folder.

PR #15603.
2021-10-22 05:51:11 +03:00
SiderealArt
090199f9de NSIS: Update Traditional Chinese translation
PR #15595.
2021-10-20 07:15:09 +03:00
Chocobo1
cd3635985e Merge pull request #15567 from Chocobo1/qt6
Fix Qt6 compiling issues
2021-10-15 11:55:13 +08:00
Chocobo1
8a7179195f Suppress remove null widget warning
And improve text format for translation work.
2021-10-14 00:25:30 +08:00
Chocobo1
e45e29b431 Avoid type specifier mismatch
Qt5 uses `int` and Qt6 uses `qsizetype`, so use the stream version of
`qDebug()` to avoid specifying types.
2021-10-14 00:24:41 +08:00
Chocobo1
ca28fc27dc Disable deprecated Qt functions
Also Qt6 by default set these attributes to our preferred value.
2021-10-13 12:00:21 +08:00
Chocobo1
08b2cde8e8 Let infohash v2 text be mouse selectable
And drop unused property.
2021-10-13 11:59:44 +08:00
Chocobo1
489d88e02a Suppress conversion warning
This fixes MSVC warning C4305: 'argument': truncation from 'double' to 'float'.
`QColor::setAlphaF()` parameter has been changed to `float` in Qt6.
2021-10-12 15:37:45 +08:00
Chocobo1
dff39ffd20 Fix typo
https://doc.qt.io/qt-6/qmake-variable-reference.html#qt-major-version
2021-10-12 14:16:53 +08:00
Chocobo1
3c948ef063 Merge pull request #15562 from Chocobo1/precommit
GHA CI: Switch to pre-commit framework for checking file health
2021-10-12 11:33:16 +08:00
Chocobo1
7087565d92 Fix typo 2021-10-11 22:17:17 +08:00
Chocobo1
3467358663 GHA CI: Switch to pre-commit framework for checking file health
Now users are able to run the same checks on their local development
environment.
https://pre-commit.com/
2021-10-11 22:17:10 +08:00
Chocobo1
45a1c25a29 Merge pull request #15467 from xavier2k6/Python_URL
Update python installer URL for Windows
2021-10-11 12:47:00 +08:00
Chocobo1
70a11a12b3 Merge pull request #15549 from Chocobo1/api
WebAPI: Use specific number to represent non-existing values
2021-10-11 12:38:38 +08:00
Vladimir Golovnev
5d5b0d572e Merge pull request #15536 from glassez/expected
Don't use output parameters for error handling
2021-10-10 15:04:55 +03:00
Chocobo1
7c8eadfddf Revert "WebUI: group trackers by hostname"
This functionality wasn't ever correctly implemented and couldn't be
done without considerable effort, so revert it for now.
This reverts commit 4ac25a50ed.
PR #15542.
2021-10-10 11:00:21 +08:00
Chocobo1
89ca0c537d Fix filename extension 2021-10-10 02:55:59 +08:00
xavier2k6
a92a6404cb Make Python minimum version requirement an argument 2021-10-09 10:45:26 +01:00
Vladimir Golovnev (Glassez)
78459fcb31 Don't throw exception in TorrentInfo::saveToFile() 2021-10-09 08:54:20 +03:00
Vladimir Golovnev (Glassez)
41fc0fd084 Don't use output parameters for error handling 2021-10-09 08:54:03 +03:00
Chocobo1
5c9655abc3 WebAPI: Use specific number to represent non-existing values
Closes #15545.
2021-10-09 12:31:15 +08:00
Chocobo1
3301797491 Merge pull request #15530 from Chocobo1/errmsg
Log error message in DownloadHandlerImpl class
2021-10-09 12:09:56 +08:00
Daniel Aleksandersen
eb5e1d34df WebUI: Add meta application name
Used for installable/pinned app installs.
PR #15539.
2021-10-09 12:06:44 +08:00
Daniel Aleksandersen
9e92e5995f WebUI: Set icon sizes attribute
Gives browsers more information to pick the best icon.
PR #15540.
2021-10-09 11:59:37 +08:00
Chocobo1
e96f2d7be0 Simplify comparison
From https://doc.qt.io/qt-5/qdatetime.html#operator-eq-eq:
>Since 5.14, all invalid datetimes are equal to one another and differ from all other datetimes.
2021-10-09 02:20:22 +08:00
Chocobo1
03cb51844b Remove redundant define
NOMINMAX is already defined in build scripts.
2021-10-08 11:11:02 +08:00
Chocobo1
6b06cc9216 Log error message in DownloadHandlerImpl class 2021-10-08 11:07:35 +08:00
Chocobo1
6b49323f05 Improve error message reporting 2021-10-08 11:01:48 +08:00
Chocobo1
4a11fab2b1 Add constexpr to Sample class functions 2021-10-08 01:04:58 +08:00
Vladimir Golovnev
c382191e75 Correctly iterate through the files of torrent
PR #15535.
2021-10-07 10:48:00 +03:00
luzpaz
4d480b8761 Fix various typos
Found via `codespell -q 3 -S *.ts`.
PR #15520.
2021-10-05 12:58:25 +08:00
Chocobo1
cd25562fd2 Merge pull request #15516 from Chocobo1/win_ci
GHA CI: build dependencies manually
2021-10-05 12:41:09 +08:00
Chocobo1
9a3d560d9e GHA CI: use larger cache for compiling
Previous default was 500MB and it couldn't fit all compile results in
it, enlarge it to 2GB. Note that 2GB isn't the actual size stored on the
server, it will be compressed again by zstd and will be a lot smaller.
2021-10-03 16:38:34 +08:00
Chocobo1
4924fb95f8 GHA CI: build dependencies manually
Now it builds boost, libtorrent manually and we have control over its
version.
Enable build matrix for libtorrent versions.
Reorganized the folder/file layout in built artifact.
2021-10-03 16:07:03 +08:00
Chocobo1
6de67fe81f Merge pull request #15511 from Chocobo1/pr
Put PR template into action
2021-10-03 12:53:19 +08:00
Vladimir Golovnev
bc71827c01 Improve torrent content handling
Hide .pad files.

PR #15468.
2021-10-02 21:42:58 +03:00
Vladimir Golovnev
a8ade3a04b Merge pull request #14465 from glassez/qt6
Allow to build with Qt6
2021-10-02 21:29:10 +03:00
Chocobo1
eca04e2e92 Revise "Opening PR" section in contributing guide 2021-10-02 23:38:38 +08:00
Chocobo1
763b9fc1da Put PR template into action 2021-10-02 15:04:23 +08:00
Vladimir Golovnev (glassez)
add75fbc77 Make complete type declarations available where needed 2021-10-02 09:23:50 +03:00
Vladimir Golovnev (Glassez)
86b1ac5d7c CMake: Allow to build with Qt6 2021-10-02 09:23:49 +03:00
Vladimir Golovnev (glassez)
b51197936b QMake: Don't use WinExtras module with Qt6 2021-10-02 09:23:15 +03:00
Jose M. Abuin
64609ce5cf Add missing double-click action
Closes #15422.
PR #15509.
2021-10-01 12:16:58 +08:00
Chocobo1
b81cbf9062 Merge pull request #15505 from Chocobo1/freebsd
Add detection for various OS
2021-10-01 11:52:48 +08:00
Chocobo1
e7e881e5d7 Merge pull request #15500 from Chocobo1/networkfs
Add remote filesystem magic numbers
2021-09-30 10:27:57 +08:00
Chocobo1
e236a76d5a Use case statement for host OS detection 2021-09-29 16:32:29 +08:00
Chocobo1
ad8a827c1f Add detection for Haiku in configure script
Patch taken from downstream:
https://github.com/haikuports/haikuports/blob/master/net-p2p/qbittorrent/patches/qbittorrent-4.3.1.patchset
2021-09-29 16:22:20 +08:00
Chocobo1
08ac33bc5c Add detection for OpenBSD in configure script
Patch taken from downstream:
http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/net/qbittorrent/qbittorrent/patches/patch-configure_ac
2021-09-29 16:22:15 +08:00
Chocobo1
5cf39a2970 Reuse code path
`buf.f_type` should be compatible across platforms.
https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/statfs.2.html
2021-09-27 22:40:25 +08:00
Chocobo1
1c9321d5a1 Add remote filesystem magic numbers 2021-09-27 22:22:36 +08:00
Chocobo1
97a8d865dc WebUI: revise hash function
In benchmark, using `Math.imul()` is about ~20% faster than floating
point multiplication.

PR #15475.
2021-09-27 13:54:28 +08:00
Chocobo1
982133d9b6 CI: Clean up scripts
* Remove redundant dependencies
* Replace ccache action provider
* Let coverity-scan script build with libtorrent v2.0.4.

PR #15483.
2021-09-23 12:38:52 +08:00
Chocobo1
d23935a269 Merge pull request #15484 from Chocobo1/perference
Clean up Preferences class
2021-09-23 12:38:12 +08:00
Chocobo1
1c343a444b Remove redundant const specifier 2021-09-21 17:19:38 +08:00
Chocobo1
1c9696b68e Suppress function unused warning 2021-09-21 17:18:50 +08:00
Chocobo1
ecd23d0abd Merge pull request #15459 from Chocobo1/i18n
Revise description wordings
2021-09-19 13:29:08 +08:00
Chocobo1
010d1b5ff8 Merge pull request #15419 from Chocobo1/file
Improve error detection when saving files
2021-09-19 13:28:47 +08:00
Chocobo1
46394a7c0f Combine stream statements 2021-09-18 12:59:08 +08:00
Chocobo1
fc86034fab WebUI: update authors page 2021-09-18 12:59:07 +08:00
Chocobo1
03012cc175 Revise description wordings 2021-09-18 12:59:07 +08:00
xavier2k6
8518333406 Replace GitHub markdown templates with GitHub forms
PR #15330.
2021-09-17 12:51:33 +08:00
Vladimir Golovnev
b2e0e25f1c Merge pull request #15451 from glassez/profile
Improve Profile handling
2021-09-15 19:11:22 +03:00
Chocobo1
9673be17cb Improve handling when writing to temporary files
Let QTemporaryFile remove incomplete written file when error occurs.
"XXXXXX" template is not strictly required according to Qt doc.
2021-09-15 23:25:27 +08:00
Chocobo1
fa8786e230 Flush manually when saving a file
This is to work around https://bugreports.qt.io/browse/QTBUG-75077
2021-09-15 23:15:33 +08:00
Chocobo1
21f72baae2 Use QSaveFile wherever applicable
expected.hpp was fetched from:
b803e3c07b/include/nonstd/expected.hpp
2021-09-15 21:54:44 +08:00
xavier2k6
4b78af268f Update python installer URL for Windows 2021-09-12 17:17:06 +01:00
jagannatharjun
a734199383 Fix startup with different profiles 2021-09-12 10:59:20 +03:00
Vladimir Golovnev (Glassez)
046b741700 Improve Profile handling
Add Profile::rootPath and Profile::configurationName properties.
2021-09-12 10:58:48 +03:00
Chocobo1
ce0b6f0d56 Merge pull request #15450 from Chocobo1/pr_15303
Fix code defects
2021-09-12 12:56:33 +08:00
Chocobo1
6de0622c1a Merge pull request #15444 from Chocobo1/guard
Prevent self-assignment in assignment operator
2021-09-12 12:56:17 +08:00
Jesse Smick
6229b81730 WebUI: Add pieces progress bar to General tab
Closes #15292.
PR #15418.
2021-09-12 12:55:41 +08:00
Chocobo1
c701379a2e Fix typo 2021-09-11 12:07:52 +08:00
Chocobo1
0783968121 Guard for null pointer 2021-09-11 11:54:01 +08:00
gxcreator
307f5e6e56 Initialize member fields 2021-09-11 11:53:41 +08:00
Chocobo1
cb29685a24 Use Qt macro to disable various constructors 2021-09-10 19:51:27 +08:00
Faisal Al-Munawar Fathur Rahman
dabba89682 Update Indonesian translation
PR #15436.
2021-09-10 09:05:40 +08:00
Chocobo1
2efd4f2a77 Prevent self-assignment in assignment operator 2021-09-09 21:42:46 +08:00
Vladimir Golovnev
90296b3ef0 Add "Skip hash check" option for watched folders
Closes #15388.
PR #15433.
2021-09-09 07:00:51 +03:00
smigii
8f02fe0cc6 Elide text from the right for all columns' header
Minimizing columns no longer truncates text from the left, now elides
text from the right for better readability. Done by setting header's
textElideMode to Qt::TextElideRight.

Fix issue #14419.
PR #15366.
2021-09-08 11:47:55 +08:00
Chocobo1
7a6edcdddb Fix broken behavior of "priority by shown file order"
Closes #15421.
PR #15423.
2021-09-07 11:47:13 +08:00
Chocobo1
81139c0098 Improve error detection when saving files 2021-09-05 12:20:22 +08:00
Chocobo1
6a6268c068 Merge pull request #15396 from jagannatharjun/downloadrequest
Fix invalid RSS feed icons
2021-09-05 12:11:57 +08:00
jagannatharjun
68133ec8e3 Correctly use fallback icons for RSS feed in GUI 2021-09-04 12:11:44 +05:30
jagannatharjun
314f92f2d8 Use DownloadRequest::destFileName for downloading RSS feed icons 2021-09-04 12:02:38 +05:30
jagannatharjun
8b5db328ec Add DownloadRequest::destFileName 2021-09-04 12:02:38 +05:30
Chocobo1
615b76f78c Prolong wait time for shutdown
The default was 90s[1], prolong to 30 mins.
From the discussion in [2], ~2k torrents took 5 mins to completely
shutdown. Here we wait at most 30 mins which scales to about 12k
torrents which should cover most use case (also note that 4.3.x series
is mentioned to have even shorter shutdown time).

[1] https://www.freedesktop.org/software/systemd/man/systemd-system.conf.html#DefaultTimeoutStartSec=
[2] https://github.com/qbittorrent/qBittorrent/issues/15381#issuecomment-912080617

PR #15414.
2021-09-04 13:20:35 +08:00
Prince Gupta
f2912c14ea Stick Unread row to top in RSS feed list
PR #15397.
2021-09-04 12:55:58 +08:00
Chocobo1
08f33d7e9e Fix WebUI crash when tracker URL is invalid
Closes #15391.
PR #15395.
2021-08-31 11:53:42 +08:00
xavier2k6
c034cb5985 Remove Windows Vista support from manifest
PR #15394.
2021-08-30 08:07:15 +03:00
Chocobo1
e3cd15dced Remove unnecessary UI properties in "Add new torrent" dialog (#15387)
Closes #15383.
2021-08-29 11:29:01 +08:00
JungHee Lee
8439d4e827 Update Korean NSIS translation (#15380) 2021-08-29 11:27:09 +08:00
sledgehammer999
2b501904cf Bump to 4.4.0beta3 2021-08-29 01:53:02 +03:00
sledgehammer999
ea986a1f1b Sync translations from Transifex and run lupdate 2021-08-29 01:51:47 +03:00
Chocobo1
b924357ea9 Specify Unicode for resource block (#15370)
The StringFileInfo block was using "1252 Multilingual", change it to
"1200 Unicode" for consistency.
https://docs.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block

Closes #15364.
2021-08-26 11:30:53 +08:00
Vladimir Golovnev
b823d74ac3 Use "old file path" provided by libtorrent
Until libtorrent provided an "old_name" field in `file_renamed_alert` we relied
on a workaround with storing old file names to remove empty leftover folders.
PR #15356.
2021-08-23 07:29:50 +03:00
Chocobo1
d1e2019cd7 Merge pull request #15357 from a1346054/master
Fix minor licensing issue
2021-08-23 11:23:02 +08:00
Chocobo1
70573eba2c GHA CI: Use preinstalled vcpkg (#15355)
This will save maintenance work on the vcpkg version.

Also a few other improvements:
* Add quotes to path
* Sort command flags
* Avoid switching shell, always use powershell (the default shell)
2021-08-22 12:29:17 +08:00
a1346054
cf46653333 Fix typo 2021-08-21 16:02:05 +00:00
a1346054
8d3fcbd897 Use license file verbatim
The GPL part of COPYING file was distributed incomplete, it must be
distributed verbatim.

The file was obtained from:
https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
2021-08-21 16:00:19 +00:00
Chocobo1
1900538315 Merge pull request #15342 from Chocobo1/macos
GHA CI: Split "main CI" to multiple workflows
2021-08-20 03:43:24 +08:00
Chocobo1
a9feae6110 GHA CI: Split "main CI" to multiple workflows 2021-08-19 11:54:58 +08:00
Chocobo1
8d822f2cb4 Improve "last activity" calculation in WebAPI (#15339)
Co-authored-by: 秦风 <mayli.he@gmail.com>

Co-authored-by: Chocobo1 <Chocobo1@users.noreply.github.com>
2021-08-19 11:16:34 +08:00
Chocobo1
c12e486f59 GHA CI: Use prebuilt library packages on macOS 2021-08-19 11:08:19 +08:00
Chocobo1
d0d0bed333 Merge pull request #15340 from Chocobo1/libt2
Support libtorrent v2.0.4 in CI
2021-08-19 10:54:01 +08:00
Chocobo1
8799321312 GHA CI: Use ccache 2021-08-18 23:06:02 +08:00
Chocobo1
00d2997971 GHA CI: Move global environment variables out 2021-08-18 23:06:02 +08:00
Chocobo1
69f7f233fd GHA CI: Remove OS variable from build matrix
It is meaningless to build on multiple linux versions as we only depend
on library versions, not OS versions.
Also remove redundant "shell default" section.
2021-08-18 23:06:02 +08:00
Chocobo1
793e8643bf GHA CI: Support libtorrent v2.0.4 2021-08-18 23:05:23 +08:00
Chocobo1
521ef8e28f Merge pull request #15321 from Chocobo1/ci
Clean up workflow files coding style
2021-08-15 12:24:21 +08:00
Chocobo1
7433d85418 Revise workflow steps name/description 2021-08-14 15:08:31 +08:00
Chocobo1
ba1cf12817 Capitalize workflow step names 2021-08-14 14:58:16 +08:00
Chocobo1
7dc7b95bfd Merge pull request #15306 from Chocobo1/icon
Minor UI revise
2021-08-14 12:12:49 +08:00
Chocobo1
59352e4ca7 Use nodejs lts version for CI
The exact nodejs version isn't critical here, just make sure it isn't
too outdated.
2021-08-13 12:23:16 +08:00
Chocobo1
011d026d76 Rename jobs 2021-08-13 12:23:16 +08:00
Chocobo1
89a8184ad2 Avoid using legacy backticks
https://github.com/koalaman/shellcheck/wiki/SC2006
2021-08-13 12:23:16 +08:00
Chocobo1
a23e10dff5 Split commands to multiple lines properly 2021-08-13 12:23:16 +08:00
Chocobo1
535603fac4 Use latest Ubuntu images
Jobs in those workflows has little to do with specific OS version, so
just use the latest.
2021-08-13 12:23:16 +08:00
Chocobo1
fb6282da57 Add proper indention to steps section
Following the style from the examples in https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idsteps
2021-08-13 12:23:15 +08:00
Chocobo1
23766cd01d Clean up Github workflows' name 2021-08-13 12:23:04 +08:00
sledgehammer999
44b2afb218 Merge pull request #15310 from Chocobo1/stale
Stale bot: Handle more PR per run
2021-08-11 17:29:45 +03:00
sledgehammer999
9e82e59fc8 Merge pull request #15315 from sledgehammer999/readme_chat
Change IRC channel to Libera.Chat
2021-08-11 17:27:50 +03:00
Chocobo1
e006538514 Fix cmake script (#15309)
Fix up 138c911ef4.
2021-08-11 13:26:15 +08:00
sledgehammer999
fad5dfa4f4 Change IRC channel to Libera.Chat
Unfortunately Freenode, after a takeover, decided to purge all their databases, thus
deleting all channel and user registrations, without a warning.
So if we're forced to re-register our stuff why not go where the cool kids are at?
2021-08-11 01:13:55 +03:00
a-raccoon
b737ee0240 Update README.md
Removed these two lines because they haven't been true in a very long time, and is no longer even possible.
```
You can also meet me (sledgehammer_999) on IRC:
`#qbittorrent on irc.freenode.net`
```
2021-08-11 00:37:03 +03:00
Chocobo1
9d9f774dad Handle more PR per run
Previously only about ~27 PR are scanned per run, this commit attempt
to handle all open PR at once.
2021-08-10 13:14:25 +08:00
Chocobo1
526ee9c9db Add tooltip to listening port spinbox 2021-08-10 12:24:50 +08:00
sledgehammer999
671eff324d Merge pull request #15298 from sledgehammer999/coverity_workflow
Run Coverity once per month
2021-08-09 21:18:12 +03:00
Chocobo1
a93cf04aca Use default values for inconsequential fields
Or when the default value is actually what we want.
2021-08-09 13:37:48 +08:00
Chocobo1
e93a67e644 Use default upper limits for ddns entries
The default is 32767 which is larger than the previous artificial limit.
2021-08-09 13:14:51 +08:00
Chocobo1
b29a52dfa8 Use the same icon for selecting folders/files
As stated in Qt doc, the `QStyle::SP_DialogOpenButton` is only for a
button within QDialogButtonBox which means it isn't suitable elsewhere.
2021-08-09 13:14:14 +08:00
Chocobo1
4ed99ba851 Introduce stale bot for PR (#15211)
Stale bot will mark stale PR with a tag and close them after a specified
interval of time.

* Draft PRs are not subject to this marking.
* PR with an assignee or milestone is not subject to this marking.
2021-08-09 11:26:04 +08:00
sledgehammer999
3e92e716b2 Merge pull request #15294 from sledgehammer999/accept_v2_infohash
Accept v2 infohashes for download
2021-08-08 12:29:04 +03:00
sledgehammer999
c6c8f6563d Run Coverity once per month 2021-08-08 12:24:19 +03:00
Chocobo1
138c911ef4 Add compile definition to indicate using of libtorrent 2.0 (#15297)
The compile definition is temporary which will be removed when qbt
ditches libtorrent 1.x.
2021-08-08 13:27:22 +08:00
sledgehammer999
e5fe6401a0 Accept v2 infohashes for download
Partially fixes #15287
2021-08-07 17:25:13 +03:00
Vladimir Golovnev
bed643e627 Delay processing of watched folders (#15282)
Fixes regression of #14882.
Closes #15272.
2021-08-07 12:33:29 +03:00
Mengyang Li
4ac25a50ed WebUI: group trackers by hostname (#15264)
Closes #13608.
2021-08-07 12:25:07 +08:00
Chocobo1
d9cf189ef6 Merge pull request #15281 from Chocobo1/lib
Update js libraries
2021-08-06 16:56:02 +08:00
Matthaiks
6e19878973 NSIS: Add Polish translation (#15262) 2021-08-06 12:16:46 +08:00
Chocobo1
9f9c4d6ed0 Ignore file heath for 3rd party libraries 2021-08-05 16:03:19 +08:00
Chocobo1
567848e94f Work around missing function error
The fix comes from older version of MochaUI.
2021-08-05 16:03:18 +08:00
Chocobo1
221cbcc1ac Update MochaUI to v0.9.7
Upstream: https://github.com/cdotyone/mochaui/blob/develop-0.9.8/Build/mocha.js
2021-08-05 16:03:07 +08:00
Chocobo1
dc2086dab4 Update clipboard.js to v2.0.8 2021-08-05 15:24:03 +08:00
Chocobo1
7be2a03c86 Update mootools to v1.6.0 2021-08-05 15:24:01 +08:00
Sylvain Finot
e87f8f5b93 Expose SSRF mitigation (#15247) 2021-08-04 11:28:36 +08:00
Chocobo1
11a063ea66 Merge pull request #15242 from Chocobo1/libt_version
Detect incompatible libtorrent v2 versions in build systems
2021-08-04 11:28:07 +08:00
sledgehammer999
1d26f4c5f7 Bump to 4.4.0beta2 2021-08-03 23:15:27 +03:00
sledgehammer999
8a09558ed8 Sync translations from Transifex and run lupdate 2021-08-03 23:14:08 +03:00
Daniel Aleksandersen
60b1e692b9 Disconnect comment links fom the WebUI (#15251) 2021-08-03 16:26:04 +08:00
Chocobo1
0a1865d0dd Don't use old style cast 2021-08-01 13:48:46 +08:00
Chocobo1
3d94c70c48 Adopt a subset of semantic versioning notation
https://github.com/npm/node-semver#ranges
https://semver.npmjs.com/
2021-08-01 13:48:22 +08:00
Chocobo1
7d7f967d5e Detect incompatible libtorrent v2 versions in build systems
Although the detection is working, the error message in autotools is not
perfect, i.e. it would only mention valid range for libtorrent v1 and it
shouldn't be a problem since v2 support isn't fully ready yet.
2021-07-31 16:59:37 +08:00
Chocobo1
ce554e6c77 Merge pull request #15229 from Chocobo1/port
Use spinbox special value to represent "Use any available port"
2021-07-30 15:19:08 +08:00
xavier2k6
5d151cca9d GitHub Actions CI: update workflow (#15245)
- update vcpkg to latest commit (includes updated BOOST)
2021-07-30 15:18:38 +08:00
Chocobo1
e47d90b5a6 Update pkg.m4 to latest version
Upstream: https://gitlab.freedesktop.org/pkg-config/pkg-config/-/blob/master/pkg.m4.in
2021-07-29 14:15:51 +08:00
AbeniMatteo
e4730191db Set default file priorities when not specified (#15190) 2021-07-29 12:20:03 +08:00
Chocobo1
49aab492e0 Use spinbox special value to represent "Use any available port"
WebAPI functionality is preserved (deprecated) for now and should be
removed in the future.
2021-07-29 11:50:52 +08:00
Chocobo1
2d4d246268 Remember last viewed page in Options dialog (#15230) 2021-07-28 11:11:00 +08:00
Chocobo1
09e558ae0b Revise checkbox label for "Use any available ports" functionality
Also reorder the checkboxes a bit.
2021-07-27 13:35:18 +08:00
Chocobo1
a3fd6633c4 Use default property for widgets 2021-07-26 12:20:46 +08:00
Vladimir Golovnev
1eb246c98b Merge pull request #15181 from glassez/qt5
Raise minimum Qt version to 5.15.2
2021-07-23 06:22:57 +03:00
Chocobo1
96e0c0df20 Improve handling for magnet URI (#15209)
This add support for magnets URI in v2 hash format.
2021-07-22 13:05:59 +08:00
thalieht
aa8f420681 Recognise v2 info-hashes in clipboard for "Add torrent link" dialog (#15206) 2021-07-20 14:30:37 +03:00
Tom Piccirello
7974b5a95c Support sorting Web UI tables via touch (#15205) 2021-07-19 14:28:04 +08:00
Vladimir Golovnev
ed4570cb4d Store minimal metadata for "restore torrent" purposes (#15191)
We can no longer save valid torrent files in the general case, because
for torrents of version 2, we need a full merkle tree to do it, but if
a torrent is added from magnet link, full merkle tree may not be available
even before the end of downloading all the data. Actually, we don't need
the full torrent file for the purposes of resuming the torrent, so we can
allow libtorrent to produce only a minimal part of the metadata as part
complete resume data, but we still want to store it in a separate file,
so we extract the resulting metadata from the complete resume data before
saving and merge it together before loading.
2021-07-19 07:59:06 +03:00
AbeniMatteo
01d851440b Add "Forced metadata downloading" state (#15185) 2021-07-17 21:33:14 +03:00
AbeniMatteo
e5943b64c1 Add filter "Checking" to side panel (#15166) 2021-07-16 14:08:10 +03:00
scootergrisen
933e56494c Update Danish translation (#15192) 2021-07-16 12:55:05 +08:00
Kristof Mattei
140e73be4e Use the URI's setData to set query data (#15187)
This allows the system to properly encode the '|', instead of passing
the '|' on in the URL, which is not allowed and breaks proxies such as
Authelia.

Then, for the purpose of generalization, I pushed this pattern through
to all places where we join items with a '|'.

This comes with the caveat that when we have individual components which
contain a '|' or any other character that is not allowed per the
HTTP standard, we still like to encode the individual components,
for example in the case of 3 strings, separated by a '|'.
If we don't do this we run into the risk that upon decoding URI finds
'|' in our original strings, which is something we don't want.

For example:
Sender:
````javascript
const arr = ["foo|1", "bar|2"];
const uri = new URI("test.html").setData(arr.join("|"));
````
Then on the receiving window, when it receives the uri and splits it, it
looks like this:
````javascript
const arr = new URI().getData('hashes').split('|');
// arr is now ["foo", "1", "bar", "2"]
````

This is why when we want to send a literal "|" we need to do
`encodeURIComponent` and `decodeURIComponent` manually on each item,
and THEN we join.

For example:
Sender:
````javascript
const arr = ["foo|1", "bar|2"];
const uri = new URI("test.html").setData(arr.map(encodeURIComponent).join("|"));
````

Receiver:
````javascript
const arr = new URI().getData('hashes').split('|').map(decodeURIComponent);
// arr is now ["foo|1", "bar|2"]
````

We don't need to with hashes as they are HEX, so no risk of any weird
characters in there.
2021-07-16 12:53:47 +08:00
Vladimir Golovnev (glassez)
960b9b855f CI: Use Qt-5.15.2 on AppVeyor 2021-07-15 10:56:50 +03:00
FranciscoPombal
1e1d55b26d CI: Use Qt 5.15.2 from third-party PPA on Linux 2021-07-15 10:56:50 +03:00
Vladimir Golovnev (glassez)
925bf7715c Disable functions deprecated in Qt 5.15.2 and earlier 2021-07-15 10:56:49 +03:00
Vladimir Golovnev (Glassez)
399d3ad85a Replace QStringRef with QStringView 2021-07-15 10:56:49 +03:00
Chocobo1
d923c03d52 Merge pull request #15186 from Piccirello/webui-table-keyboard-nav
Support navigating Web UI tables with arrow keys
2021-07-13 11:26:50 +08:00
Vladimir Golovnev
699b91ab8d Properly create "clean path" for watched folder (#15179) 2021-07-12 11:44:52 +03:00
AbeniMatteo
abd6eb2ff3 Add context menu to toggle content tab columns (#15164) 2021-07-12 13:57:17 +08:00
Thomas Piccirello
32f29e72c6 Support expanding/collapsing Web UI folders with arrow keys 2021-07-11 03:01:31 -07:00
Thomas Piccirello
e76bac4131 Support navigating Web UI tables with arrow keys
This allows navigating rows via up/down arrow keys.
2021-07-11 03:01:31 -07:00
Thomas Piccirello
8b94642ab1 Always set Web UI row id as a string
This helps ensure consistent behavior when performing rowId comparisons against strings.
2021-07-10 11:50:48 -07:00
Chocobo1
d3497148c5 Merge pull request #15176 from Chocobo1/tooltip
Add tooltip for more widgets
2021-07-09 16:48:58 +08:00
Vladimir Golovnev (glassez)
27baa55443 Raise minimum Qt version to 5.15.2 2021-07-09 07:41:13 +03:00
Chocobo1
fd3d4d479a Suppress type narrowing warning on MSVC
Fix up 45e31a153c.
2021-07-08 14:25:39 +08:00
Chocobo1
4b0a2d050a Display tooltip for all columns in torrent content widget
It is primary useful for showing long file names.
2021-07-08 14:23:30 +08:00
Chocobo1
d85c14864b Add tooltip for "client ID" column
Sometimes the client ID could be quite long and this patch helps showing
it.
2021-07-08 14:22:59 +08:00
Chocobo1
ee696e6f36 Merge pull request #15170 from Chocobo1/tooltip
Add tooltip for various columns
2021-07-08 13:01:59 +08:00
Chocobo1
6ccbd8472c Merge pull request #15161 from Chocobo1/helper
Use `underlying_type` member directly
2021-07-08 13:01:46 +08:00
Chocobo1
8ec26e9ea9 Don't use old style casts
Ref: https://github.com/qbittorrent/qBittorrent/runs/2996702005?check_suite_focus=true#step:8:298
2021-07-07 14:44:39 +08:00
Chocobo1
45e31a153c Reserve space for vector 2021-07-07 14:20:27 +08:00
Chocobo1
7c23d800e6 Use underlying_type member directly
`LTUnderlyingType` served as a intermediate type for libtorrent 1.1 and
1.2 and is obsoleted now.
Also add helper to convert to underlying type.
2021-07-07 14:19:17 +08:00
Chocobo1
4dbf6af733 Simplify initialization statement 2021-07-07 13:20:13 +08:00
Chocobo1
bdc03b1c75 Add tooltip for various columns
Those strings sometimes are quite long and having a tooltip would
save the action of resizing the column width to see the full message.
The WebUI already has it done for all columns.
2021-07-07 13:19:29 +08:00
AbeniMatteo
9bfc74a1bc Filter torrent info endpoint by tag (#15152) 2021-07-05 13:55:49 +08:00
Vladimir Golovnev
5d03917877 Use torrent info with hashes for creating .torrent file (#15138) 2021-07-04 09:29:34 +03:00
Vladimir Golovnev (Glassez)
d2f975a0f3 Don't forget to start "watch timer" 2021-07-02 08:34:31 +03:00
Chocobo1
eedd47860a Merge pull request #15142 from Chocobo1/warning
Use proper signed number type
2021-07-01 11:35:04 +08:00
sledgehammer999
6e59248ea6 Merge pull request #15093 from FranciscoPombal/bump_libtorrent
Raise minimum libtorrent version to 1.2.14 (2.0.4)
2021-06-30 22:39:53 +03:00
Chocobo1
365554d064 Use proper signed number type
This also suppress the compiler warning:
src/base/bittorrent/torrentimpl.cpp:228:36: warning: comparison of integer expressions of different signedness: ‘int’ and ‘const size_t’ {aka ‘const long unsigned int’} [-Wsign-compare]
2021-06-29 18:04:33 +08:00
Chocobo1
70d1cb86fd Disable move constructor where it is sensible 2021-06-29 14:49:45 +08:00
FranciscoPombal
ccb7c0d579 Raise minimum libtorrent version to 1.2.14 (2.0.4)
- Also update vcpkg to latest commit: includes libtorrent 1.2.14,
Qt 5.15.2, and Qt 6.1.1
2021-06-28 23:04:47 +01:00
sledgehammer999
fd9941e2d8 Merge pull request #15131 from FranciscoPombal/goodbye_travis
Remove TravisCI config
2021-06-28 21:48:30 +03:00
FranciscoPombal
2f89563fca Remove TravisCI config 2021-06-28 10:41:16 +01:00
sledgehammer999
261f601bd5 Merge pull request #15129 from adem4ik/patch-5
Remove excess space in torrentfileswatcher.cpp
2021-06-27 15:32:50 +03:00
Andrei Stepanov
5157e4965a Remove excess space 2021-06-27 13:01:03 +04:00
417 changed files with 81076 additions and 80034 deletions

View File

@@ -43,7 +43,7 @@ install:
before_build:
# setup env
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
- SET PATH=%PATH%;c:\qbt\qt5_64\bin;%CACHE_DIR%\jom;
- SET PATH=%PATH%;C:\Qt\5.15.2\msvc2019_64\bin;%CACHE_DIR%\jom
# setup project
- COPY /Y "%CACHE_DIR%\conf.pri" "%REPO_DIR%"
# workarounds

View File

@@ -1,89 +0,0 @@
---
name: Bug Report
about: Report a problem with qBittorrent to help us resolve it.
title: ''
labels: ''
assignees: ''
---
# Bug report
<!--
###############################################################################
WARNING!
IGNORING THE INSTRUCTIONS IN THIS TEMPLATE WILL RESULT IN THE ISSUE BEING
CLOSED AS INCOMPLETE/INVALID
###############################################################################
-->
## Checklist
<!--
################################## IMPORTANT ##################################
As you read and fulfill each of the following requirements below,
put an "x" between the square brackets to mark each task as done, like so: [x]
-->
- [ ] I have read the **issue reporting section** in the [contributing guidelines](https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md), so I know how to submit a good bug report with the required information
- [ ] I have verified that the **issue is not fixed and is reproducible** in the **[latest version](https://www.qbittorrent.org/download.php)**
- [ ] (optional but recommended) I have verified that the **issue is not fixed and is reproducible** in the **[latest CI build](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci.yaml?query=branch%3Amaster+event%3Apush)**
- [ ] I have **checked the [frequent/common issues list](https://github.com/qbittorrent/qBittorrent/projects/2)** and **searched** the issue tracker for similar bug reports (including closed ones) **to avoid posting a duplicate**
- [ ] This report is **not a support request or question**, both of which are better suited for either the [discussions section](https://github.com/qbittorrent/qBittorrent/discussions), [forum](https://qbforums.shiki.hu/), or [subreddit](https://www.reddit.com/r/qBittorrent/). The [wiki](https://github.com/qbittorrent/qBittorrent/wiki) did not contain a suitable solution either
- [ ] I have **pasted/attached the settings file and relevant log(s)** in the **Attachments** section at the bottom of the report. Mandatory: the settings file and at least the most recent log. See [this wiki page](https://github.com/qbittorrent/qBittorrent/wiki/Frequently-Asked-Questions#Where_does_qBittorrent_save_its_settings) if you're not sure where to find them.
## Description
<!--
################################## IMPORTANT ##################################
Delete each "(type here)" indicator and type your text in their place in the subsections below.
You MUST fill in ALL subsections marked with "(type here)" with the appropriate information.
Please make sure the description is worded well enough to be understood.
Provide steps to reproduce the issue, any additional relevant information, suggested solution (if applicable) and as much context and examples as possible.
For more information consult the Contributing Guidelines at https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md.
Do not forget about the mandatory attachments!
Use the Preview tab before posting to make sure your report looks like it is formatted properly.
You don't need to delete these comments, they won't show up in the final post.
-->
### qBittorrent info and operating system(s)
<!--
IMPORTANT:
if you did not get the qBittorrent installer from the links in the official website
or if you did not install it from the PPA, please mention that after the version
-->
- qBittorrent version: (type here)
- Operating system(s) where the issue occurs: (type here)
### If on Linux, `libtorrent-rasterbar` and `Qt` versions
- Qt: (type here)
- libtorrent-rasterbar: (type here)
### What is the problem
(type here)
### Detailed steps to reproduce the problem
1. (type here)
2. (type here)
3. (etc.)
### What is the expected behavior
(type here)
### Extra info (if any)
(type here)
## Attachments
<!-- paste file contents here (or attach the files if they are big), do NOT link to external sites -->

76
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: Bug Report
description: File a bug report to help improve qBittorrent user experience.
body:
- type: markdown
attributes:
value: |
#### ADVISORY
"We do not support any versions older than the current release series"
"We do not support any 3rd party/forked versions e.g. `portableapps`/`Enhanced Edition`etc."
"Please post all details in **English**."
#### Prerequisites before submitting an issue!
- Read the issue reporting section in the **[contributing guidelines](https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md)**, to know how to submit a good bug report with the required information.
- Verify that the issue is not fixed and is reproducible in the **[latest official qBittorrent version](https://www.qbittorrent.org/download.php).**
- (Optional, but recommended) Verify that the issue is not fixed and is reproducible in the latest CI (**[macOS](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci_macos.yaml?query=branch%3Amaster+event%3Apush)** / **[Ubuntu](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci_ubuntu.yaml?query=branch%3Amaster+event%3Apush)** / **[Windows](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci_windows.yaml?query=branch%3Amaster+event%3Apush)**) builds.
- Check the **[frequent/common issues list](https://github.com/qbittorrent/qBittorrent/projects/2)** and perform a **[search of the issue tracker (including closed ones)](https://github.com/qbittorrent/qBittorrent/issues)** to avoid posting a duplicate.
- Make sure this is not a support request or question, both of which are better suited for either the **[discussions section](https://github.com/qbittorrent/qBittorrent/discussions)**, **[forum](https://qbforums.shiki.hu/)**, or **[subreddit](https://www.reddit.com/r/qBittorrent/)**.
- Verify that the **[wiki](https://github.com/qbittorrent/qBittorrent/wiki)** did not contain a suitable solution either.
- If relevant to issue/when asked, the qBittorrent preferences file, qBittorrent.log & watched_folders.json (if using "Watched Folders" feature) must be provided.
See **[Where does qBittorrent save its settings?](https://github.com/qbittorrent/qBittorrent/wiki/Frequently-Asked-Questions#Where_does_qBittorrent_save_its_settings)**
- type: textarea
attributes:
label: qBittorrent & operating system versions
description: |
Qt and libtorrent-rasterbar versions are required when: 1. You are using linux. 2. You are not using an official build downloaded from our website.
Example of preferred formatting:
qBittorrent: 4.3.7 x64
Operating system: Windows 10 Pro 21H1/2009 x64
Qt: 5.15.2
libtorrent-rasterbar: 1.2.14
placeholder: |
qBittorrent:
Operating system:
Qt:
libtorrent-rasterbar:
validations:
required: true
- type: textarea
attributes:
label: What is the problem?
description: Please add the "crash report" (if encountered) or give a clear and concise description of problem.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: Please provide reliable steps to reproduce the problem.
placeholder: |
1. First step
2. Second step
3. and so on...
validations:
required: false
- type: textarea
attributes:
label: Additional context
description: Add screenshots etc. (Anything that will provide more context about the problem)
validations:
required: false
- type: textarea
attributes:
label: Log(s) & preferences file(s)
description: |
Add these files: qBittorrent preferences file, qBittorrent.log & watched_folders.json (if using "Watched Folders" feature).
See **[Where does qBittorrent save its settings?](https://github.com/qbittorrent/qBittorrent/wiki/Frequently-Asked-Questions#Where_does_qBittorrent_save_its_settings)**
#### Note: It's the user's responsibility to redact any sensitive information
validations:
required: false

View File

@@ -1,20 +1,17 @@
blank_issues_enabled: false
contact_links:
-
about: "Consult the wiki first (especially the FAQ), it might already contain the information you are looking for"
name: Wiki
- name: Wiki
url: "https://github.com/qbittorrent/qBittorrent/wiki/"
about: "Consult the wiki first (especially the FAQ), it might already contain the information you are looking for"
-
about: "Please ask questions related to usage/setup/support/non-issue development discussion in the Discussions section"
name: Question
- name: Question
url: "https://github.com/qbittorrent/qBittorrent/discussions"
about: "Please ask questions related to usage/setup/support/non-issue development discussion in the Discussions section"
-
about: "Alternatively, ask on the official forum"
name: Question
- name: Question
url: "http://forum.qbittorrent.org/"
-
about: "Alternatively, use the subreddit"
name: Question
about: "Alternatively, ask on the official forum"
- name: Question
url: "https://www.reddit.com/r/qBittorrent/"
about: "Alternatively, use the subreddit"

View File

@@ -1,61 +0,0 @@
---
name: Feature Request
about: Suggest a new feature or enhancement for qBittorrent.
title: ''
labels: 'Feature request'
assignees: ''
---
# Feature request
<!--
###############################################################################
WARNING!
IGNORING THE INSTRUCTIONS IN THIS TEMPLATE WILL RESULT IN THE ISSUE BEING
CLOSED AS INCOMPLETE/INVALID
###############################################################################
-->
## Checklist
<!--
################################## IMPORTANT ##################################
As you read and fulfill each of the following requirements below,
put an "x" between the square brackets to mark each task as done, like so: [x]
-->
- [ ] I have read the **feature request section** in the [contributing guidelines](https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md), so I know how to submit a good feature request with the required information
- [ ] I have verified that the **feature** I am requesting is **not available** in the **[latest version](https://www.qbittorrent.org/download.php)**
- [ ] (optional but recommended) I have verified that the **feature** I am requesting is **not available** in the **[latest CI build](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci.yaml?query=branch%3Amaster+event%3Apush)**
- [ ] I have **checked the [frequent/common issues list](https://github.com/qbittorrent/qBittorrent/projects/2)** and **searched** the issue tracker for similar feature requests (including closed ones) **to avoid posting a duplicate**
- [ ] This request is **not a support request or question**, both of which are better suited for either the [discussions section](https://github.com/qbittorrent/qBittorrent/discussions), [forum](https://qbforums.shiki.hu/), or [subreddit](https://www.reddit.com/r/qBittorrent/). The [wiki](https://github.com/qbittorrent/qBittorrent/wiki) did not contain a suitable solution either
## Description
<!--
################################## IMPORTANT ##################################
Delete each "(type here)" indicator and type your text in their place in the subsections below.
You MUST fill in ALL subsections marked with "(type here)" with the appropriate information.
Please make sure the description is worded well enough to be understood.
Provide a detailed description of the feature and as much context and examples as necessary.
If the feature request has to do with visual elements and the GUI, images/screenshots are always helpful.
For more information consult the Contributing Guidelines at https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md.
Use the Preview tab before posting to make sure your report looks like it is formatted properly.
You don't need to delete these comments, they won't show up in the final post.
-->
### Suggestion
(type here)
### Use case
(type here)
### Extra info/examples/attachments
<!-- optional -->

View File

@@ -0,0 +1,37 @@
name: Feature Request
description: Suggest a new feature or enhancement for qBittorrent.
labels: ["Feature request"]
body:
- type: markdown
attributes:
value: |
#### ADVISORY
"Please post all details in **English**."
#### Prerequisites before submitting a feature request!
- Read the feature request section in the **[contributing guidelines](https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md)**, to know how to submit a good feature request with the required information.
- Verify that the feature being requested is not available in the **[latest official qBittorrent version](https://www.qbittorrent.org/download.php).**
- (Optional but recommended) Verify that the feature being requested is not available in the latest CI (**[macOS](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci_macos.yaml?query=branch%3Amaster+event%3Apush)** / **[Ubuntu](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci_ubuntu.yaml?query=branch%3Amaster+event%3Apush)** / **[Windows](https://github.com/qbittorrent/qBittorrent/actions/workflows/ci_windows.yaml?query=branch%3Amaster+event%3Apush)**) builds.
- Search the issue tracker with the **[feature request filter](https://github.com/qbittorrent/qBittorrent/issues?q=is%3Aopen+is%3Aissue+label%3A%22Feature+request%22)** for similar feature requests (including closed ones) to avoid posting a duplicate.
- Make sure this is not a support request or question, both of which are better suited for either the **[discussions section](https://github.com/qbittorrent/qBittorrent/discussions)**, **[forum](https://qbforums.shiki.hu/)**, or **[subreddit](https://www.reddit.com/r/qBittorrent/)**.
- Verify that the **[wiki](https://github.com/qbittorrent/qBittorrent/wiki)** did not contain a suitable solution either.
- type: textarea
attributes:
label: Suggestion
validations:
required: false
- type: textarea
attributes:
label: Use case
validations:
required: false
- type: textarea
attributes:
label: Extra info/examples/attachments
description: Add screenshots etc. (Anything that will give us more context about what is being requested!)
validations:
required: false

5
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,5 @@
<!--
MANDATORY Before submitting your work, make sure you have:
1. Read https://github.com/qbittorrent/qBittorrent/blob/master/CONTRIBUTING.md#opening-a-pull-request
2. Delete this comment block
-->

View File

@@ -1,16 +0,0 @@
# Changes proposed in this pull request
<!--
IMPORTANT: an image is worth a thousand words.
It is often a good idea to post screenshots showing the "before" and "after" your PR's changes,
especially with changes related to the GUI, along with the textual description.
Images makes it immediately clearer for others what your proposed changes are all about.
-->
(type here)
<!--
OPTIONAL: if this PR directly addresses an issue, make sure to include a "Closes #XXXXX" statement at the end.
-->
<!-- You don't need to delete these comments before posting, they won't show up in the post :) -->

68
.github/workflows/check_translation_tag.py vendored Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env python3
# A pre-commit hook for detecting problematic <translation> tags
# Copyright (C) 2021 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.
from typing import Optional, Sequence
import argparse
import re
def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check')
args = parser.parse_args(argv)
error_msg = ""
regex = re.compile(r"\s*</translation>")
for filename in args.filenames:
line_counter = 1
error_buffer = ""
with open(filename) as file:
try:
for line in file:
if (match := regex.match(line)) is not None:
error_buffer += str(f"Defect file: \"{filename}\"\n"
f"Line: {line_counter}\n"
f"Column span: {match.span()}\n"
f"Part: \"{match.group()}\"\n\n")
line_counter += 1
except UnicodeDecodeError as error:
# not a text file, skip
continue
error_msg += error_buffer
if len(error_msg) > 0:
print(error_msg)
return 1
return 0
if __name__ == '__main__':
exit(main())

View File

@@ -1,254 +0,0 @@
name: GitHub Actions CI
# Cache is used for all Windows and macOS dependencies (size approx. 1230 * 2 + 1850 = 4310 MiB)
# Cache is not used for Ubuntu builds, because it already has all dependencies except
# the appropriate libtorrent version, which only takes 3-5 minutes to build from source anyway
on: [pull_request, push]
env:
VCPKG_COMMIT: 8f03e2264da6b95fa5b01dd89cdd5b499458d428
VCPKG_DEST_MACOS: /Users/runner/qbt_tools/vcpkg
VCPKG_DEST_WIN: C:\qbt_tools\vcpkg
LIBTORRENT_VERSION_TAG: v1.2.13
jobs:
ci_ubuntu:
name: Ubuntu
strategy:
matrix:
os: [ubuntu-20.04]
qbt_gui: ["GUI=ON", "GUI=OFF"]
fail-fast: false
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- name: checkout repository
uses: actions/checkout@v2
- name: install all build dependencies except libtorrent from Ubuntu repos
run: |
sudo apt update
sudo apt install \
build-essential cmake git ninja-build pkg-config \
libssl-dev libgeoip-dev zlib1g-dev \
libboost-dev libboost-chrono-dev libboost-random-dev libboost-system-dev
# sudo apt install libqt5svg5-dev qtbase5-dev qttools5-dev # the Qt version in the standard repositories is too old...
# this will be installed under /opt/qt514. CMake will still find it automatically without additional hints
# to speed up the process, only the required components are installed rather than the full qt514-meta-full metapackage
- name: install Qt 5.14.2 from an external PPA
run: |
sudo add-apt-repository ppa:beineri/opt-qt-5.14.2-focal
sudo apt install qt514base qt514svg qt514tools
- name: install libtorrent from source
run: |
git clone https://github.com/arvidn/libtorrent && cd libtorrent
git checkout ${{ env.LIBTORRENT_VERSION_TAG }}
cmake -B cmake-build-dir -G "Ninja" \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-Ddeprecated-functions=OFF \
--graphviz=cmake-build-dir/target_graph.dot
cmake --build cmake-build-dir
sudo cmake --install cmake-build-dir --prefix /usr/local
- name: build qBittorrent
run: |
cmake -B build -G "Ninja" \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-D${{ matrix.qbt_gui }} \
-DVERBOSE_CONFIGURE=ON \
--graphviz=build/target_graph.dot
cmake --build build
- name: install qBittorrent
run: sudo cmake --install build --prefix /usr/local
- name: upload artifact as zip
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_${{ matrix.os }}-x64_${{ matrix.qbt_gui }}
path: |
build/compile_commands.json
build/install_manifest.txt
build/target_graph.dot
build/qbittorrent
build/qbittorrent-nox
libtorrent/cmake-build-dir/compile_commands.json
libtorrent/cmake-build-dir/target_graph.dot
ci_windows:
name: Windows + vcpkg
runs-on: windows-2019
defaults:
run:
shell: pwsh
steps:
- name: checkout repository
uses: actions/checkout@v2
# - ninja is needed for building qBittorrent (because it's preferable, not a hard requirement)
- name: install additional required packages with chocolatey
run: |
choco install ninja
- name: setup vcpkg (cached, if possible)
uses: lukka/run-vcpkg@v7
with:
vcpkgDirectory: ${{ env.VCPKG_DEST_WIN }}
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
setupOnly: true
# Tell vcpkg to only build Release variants of the dependencies
- name: configure vcpkg triplet overlay for release builds only
run: |
New-Item -Path ${{ github.workspace }} -Name "triplets_overlay" -ItemType Directory
Copy-Item ${{ env.RUNVCPKG_VCPKG_ROOT }}/triplets/x64-windows-static.cmake `
${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake
Add-Content ${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake `
-Value "set(VCPKG_BUILD_TYPE release)"
# clear buildtrees after each package installation to reduce disk space requirements
- name: install dependencies via vcpkg
run: |
$packages = `
"boost-circular-buffer:x64-windows-static-release",
"libtorrent:x64-windows-static-release",
"qt5-base:x64-windows-static-release",
"qt5-svg:x64-windows-static-release",
"qt5-tools:x64-windows-static-release",
"qt5-winextras:x64-windows-static-release"
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe upgrade `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--no-dry-run
foreach($package in $packages)
{
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe install $package `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--clean-after-build
}
# NOTE: this is necessary to correctly find and use cl.exe with the Ninja generator for now
- name: setup devcmd
uses: ilammy/msvc-dev-cmd@v1
- name: build qBittorrent
shell: cmd
run: |
cmake -B build -G "Ninja" ^
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo ^
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_DEST_WIN }}\scripts\buildsystems\vcpkg.cmake ^
-DVCPKG_TARGET_TRIPLET=x64-windows-static-release ^
-DVERBOSE_CONFIGURE=ON ^
-DMSVC_RUNTIME_DYNAMIC=OFF ^
--graphviz=build\target_graph.dot
cmake --build build
- name: upload artifact as zip
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_Windows-x64
path: |
build/compile_commands.json
build/target_graph.dot
build/qbittorrent.exe
build/qbittorrent.pdb
dist/windows/qt.conf
ci_macos:
name: macOS + vcpkg
strategy:
matrix:
qbt_gui: ["GUI=ON", "GUI=OFF"]
fail-fast: false
runs-on: macos-10.15
defaults:
run:
shell: pwsh
steps:
- name: checkout repository
uses: actions/checkout@v2
# - ninja is needed for building qBittorrent (because it's preferable, not a hard requirement)
# - automake is needed for the installation the vcpkg installation of fontconfig, a dependency of qt5-base
- name: install additional required packages with homebrew
shell: bash
run: |
brew install automake ninja
- name: setup vcpkg (cached, if possible)
uses: lukka/run-vcpkg@v7
with:
vcpkgDirectory: ${{ env.VCPKG_DEST_MACOS }}
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
setupOnly: true
- name: configure vcpkg triplet overlay for release builds only
run: |
New-Item -Path ${{ github.workspace }} -Name "triplets_overlay" -ItemType Directory
Copy-Item ${{ env.RUNVCPKG_VCPKG_ROOT }}/triplets/x64-osx.cmake `
${{ github.workspace }}/triplets_overlay/x64-osx-release.cmake
Add-Content ${{ github.workspace }}/triplets_overlay/x64-osx-release.cmake `
-Value "set(VCPKG_BUILD_TYPE release)","set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15)"
# NOTE: Avoids a libtorrent ABI issue. See https://github.com/arvidn/libtorrent/issues/4965
- name: force AppleClang to compile libtorrent with the same C++ standard as qBittorrent
run: |
(Get-Content -path ${{ env.RUNVCPKG_VCPKG_ROOT }}/ports/libtorrent/portfile.cmake).Replace( `
'${FEATURE_OPTIONS}', '${FEATURE_OPTIONS} -DCMAKE_CXX_STANDARD=17') `
| Set-Content -Path ${{ env.RUNVCPKG_VCPKG_ROOT }}/ports/libtorrent/portfile.cmake
- name: install dependencies via vcpkg
run: |
$packages = `
"boost-circular-buffer:x64-osx-release",
"libtorrent:x64-osx-release",
"qt5-base:x64-osx-release",
"qt5-svg:x64-osx-release",
"qt5-tools:x64-osx-release",
"qt5-macextras:x64-osx-release"
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg upgrade `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--no-dry-run
foreach($package in $packages)
{
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg install $package `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--clean-after-build
}
- name: build qBittorrent
shell: bash
run: |
cmake -B build -G "Ninja" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_DEST_MACOS }}/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=x64-osx-release \
-D${{ matrix.qbt_gui }} \
-DVERBOSE_CONFIGURE=ON \
--graphviz=build/target_graph.dot
cmake --build build
- name: upload artifact as zip
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_macOS_${{ matrix.qbt_gui }}
path: |
build/compile_commands.json
build/target_graph.dot
build/qbittorrent.app
build/qbittorrent-nox.app

17
.github/workflows/ci_file_health.yaml vendored Normal file
View File

@@ -0,0 +1,17 @@
name: CI - File health
on: [pull_request, push]
jobs:
ci:
name: Check
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install tools
uses: actions/setup-python@v2
- name: Check files
uses: pre-commit/action@v2.0.3

91
.github/workflows/ci_macos.yaml vendored Normal file
View File

@@ -0,0 +1,91 @@
name: CI - macOS
on: [pull_request, push]
jobs:
ci:
name: Build
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
libt_version: ["2.0.4", "1.2.14"]
qbt_gui: ["GUI=ON", "GUI=OFF"]
qt_version: ["5.15.2", "6.2.0"]
exclude:
- libt_version: "1.2.14"
qt_version: "6.2.0"
env:
openssl_root: /usr/local/opt/openssl@1.1
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install dependencies
run: |
brew update > /dev/null
brew install \
cmake ninja \
boost openssl@1.1 zlib
- name: Setup ccache
uses: Chocobo1/setup-ccache-action@v1
with:
update_packager_index: false
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: ${{ matrix.qt_version }}
- name: Install libtorrent
run: |
git clone --branch v${{ matrix.libt_version }} --depth 1 https://github.com/arvidn/libtorrent.git
cd libtorrent
git submodule update --init --recursive
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_STANDARD=17 \
-Ddeprecated-functions=OFF \
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}"
cmake --build build
sudo cmake --install build
- name: Build qBittorrent (Qt5)
if: ${{ startsWith(matrix.qt_version, 5) }}
run: |
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}" \
-DQt5_DIR="$Qt5_DIR" \
-DVERBOSE_CONFIGURE=ON \
-D${{ matrix.qbt_gui }}
cmake --build build
- name: Build qBittorrent (Qt6)
if: ${{ startsWith(matrix.qt_version, 6) }}
run: |
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DOPENSSL_ROOT_DIR="${{ env.openssl_root }}" \
-DQT6=ON \
-DQt6_DIR="$Qt6_DIR" \
-DVERBOSE_CONFIGURE=ON \
-D${{ matrix.qbt_gui }}
cmake --build build
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_macOS_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}_Qt-${{ matrix.qt_version }}
path: |
build/qbittorrent.app
build/qbittorrent-nox.app

99
.github/workflows/ci_ubuntu.yaml vendored Normal file
View File

@@ -0,0 +1,99 @@
name: CI - Ubuntu
on: [pull_request, push]
jobs:
ci:
name: Build
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
libt_version: ["2.0.4", "1.2.14"]
qbt_gui: ["GUI=ON", "GUI=OFF"]
qt_version: ["5.15.2", "6.2.0"]
exclude:
- libt_version: "1.2.14"
qt_version: "6.2.0"
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt update
sudo apt install \
build-essential cmake ninja-build pkg-config \
libboost-dev libssl-dev zlib1g-dev
- name: Setup ccache
uses: Chocobo1/setup-ccache-action@v1
with:
update_packager_index: false
ccache_options: |
max_size=2G
- name: Install Qt
uses: jurplel/install-qt-action@v2
with:
version: ${{ matrix.qt_version }}
- name: Install libtorrent
run: |
git clone --branch v${{ matrix.libt_version }} --depth 1 https://github.com/arvidn/libtorrent.git
cd libtorrent
git submodule update --init --recursive
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-Ddeprecated-functions=OFF \
--graphviz=cmake-build-dir/target_graph.dot
cmake --build build
sudo cmake --install build
- name: Build qBittorrent (Qt5)
if: ${{ startsWith(matrix.qt_version, 5) }}
run: |
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DQt5_DIR="$Qt5_DIR" \
-D${{ matrix.qbt_gui }} \
-DVERBOSE_CONFIGURE=ON \
--graphviz=build/target_graph.dot
cmake --build build
sudo cmake --install build
- name: Build qBittorrent (Qt6)
if: ${{ startsWith(matrix.qt_version, 6) }}
run: |
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DQt6_DIR="$Qt6_DIR" \
-DQT6=ON \
-D${{ matrix.qbt_gui }} \
-DVERBOSE_CONFIGURE=ON \
--graphviz=build/target_graph.dot
cmake --build build
sudo cmake --install build
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_ubuntu-20.04-x64_${{ matrix.qbt_gui }}_libtorrent-${{ matrix.libt_version }}_Qt-${{ matrix.qt_version }}
path: |
build/compile_commands.json
build/install_manifest.txt
build/target_graph.dot
build/qbittorrent
build/qbittorrent-nox
libtorrent/cmake-build-dir/compile_commands.json
libtorrent/cmake-build-dir/target_graph.dot

32
.github/workflows/ci_webui.yaml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: CI - WebUI
on: [pull_request, push]
jobs:
ci:
name: Check
runs-on: ubuntu-latest
defaults:
run:
working-directory: src/webui/www
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup nodejs
uses: actions/setup-node@v2
with:
node-version: 'lts/*'
- name: Install tools
run: npm install
- name: Lint code
run: npm run lint
- name: Format code
run: |
npm run format
git diff --exit-code

128
.github/workflows/ci_windows.yaml vendored Normal file
View File

@@ -0,0 +1,128 @@
name: CI - Windows
on: [pull_request, push]
jobs:
ci:
name: Build
runs-on: windows-latest
strategy:
matrix:
libt_version: ["v2.0.4", "v1.2.14"]
fail-fast: false
env:
boost_path: "${{ github.workspace }}/boost"
libtorrent_path: "${{ github.workspace }}/libtorrent"
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Setup devcmd
uses: ilammy/msvc-dev-cmd@v1
- name: Install build tools
run: |
choco install ninja
# use the preinstalled vcpkg from image
# https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#package-management
- name: Setup vcpkg
uses: lukka/run-vcpkg@v7
with:
vcpkgDirectory: C:/vcpkg
doNotUpdateVcpkg: true # the preinstalled vcpkg is updated regularly
setupOnly: true
# tell vcpkg to only build Release variants of the dependencies
- name: Configure vcpkg triplet overlay
run: |
New-Item `
-Path "${{ github.workspace }}" `
-Name "triplets_overlay" `
-ItemType Directory
Copy-Item `
"${{ env.RUNVCPKG_VCPKG_ROOT }}/triplets/x64-windows-static.cmake" `
"${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake"
Add-Content `
"${{ github.workspace }}/triplets_overlay/x64-windows-static-release.cmake" `
-Value "set(VCPKG_BUILD_TYPE release)"
# clear buildtrees after each package installation to reduce disk space requirements
- name: Install dependencies
run: |
$packages = `
"openssl:x64-windows-static-release",
"qt5-base:x64-windows-static-release",
"qt5-svg:x64-windows-static-release",
"qt5-tools:x64-windows-static-release",
"qt5-winextras:x64-windows-static-release",
"zlib:x64-windows-static-release"
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe upgrade `
--overlay-triplets="${{ github.workspace }}/triplets_overlay" `
--no-dry-run
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe install `
--overlay-triplets="${{ github.workspace }}/triplets_overlay" `
--clean-after-build `
$packages
- name: Install boost
run: |
aria2c `
"https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.7z" `
-d "${{ runner.temp }}" `
-o "boost.7z"
7z x "${{ runner.temp }}/boost.7z" -o"${{ github.workspace }}/.."
move "${{ github.workspace }}/../boost_*" "${{ env.boost_path }}"
- name: Install libtorrent
run: |
git clone --branch ${{ matrix.libt_version }} --depth 1 https://github.com/arvidn/libtorrent.git
cd libtorrent
git submodule update --init --recursive
cmake `
-B build `
-G "Ninja" `
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
-DCMAKE_INSTALL_PREFIX="${{ env.libtorrent_path }}" `
-DCMAKE_TOOLCHAIN_FILE="${{ env.RUNVCPKG_VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake" `
-DBOOST_ROOT="${{ env.boost_path }}" `
-DBUILD_SHARED_LIBS=OFF `
-Ddeprecated-functions=OFF `
-Dstatic_runtime=ON `
-DVCPKG_TARGET_TRIPLET=x64-windows-static-release
cmake --build build
cmake --install build
- name: Build qBittorrent
run: |
cmake `
-B build `
-G "Ninja" `
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON `
-DCMAKE_TOOLCHAIN_FILE="${{ env.RUNVCPKG_VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake" `
-DBOOST_ROOT="${{ env.boost_path }}" `
-DLibtorrentRasterbar_DIR="${{ env.libtorrent_path }}/lib/cmake/LibtorrentRasterbar" `
-DMSVC_RUNTIME_DYNAMIC=OFF `
-DVCPKG_TARGET_TRIPLET=x64-windows-static-release `
-DVERBOSE_CONFIGURE=ON `
--graphviz=build/target_graph.dot
cmake --build build
- name: Prepare build artifacts
run: |
mkdir upload
copy build/qbittorrent.exe upload
copy build/qbittorrent.pdb upload
copy dist/windows/qt.conf upload
mkdir upload/cmake
copy build/compile_commands.json upload/cmake
copy build/target_graph.dot upload/cmake
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: qBittorrent-CI_Windows-x64_libtorrent-${{ matrix.libt_version }}
path: upload

67
.github/workflows/coverity-scan.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Coverity Scan
on:
schedule:
- cron: '0 0 1 * *' # Monthly (1st day of month at midnight)
workflow_dispatch: # Mainly for testing. Don't forget the Coverity usage limits.
jobs:
coverity_scan:
name: Scan
runs-on: ubuntu-20.04
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo add-apt-repository ppa:beineri/opt-qt-5.15.2-focal
sudo apt update
sudo apt install \
build-essential cmake ninja-build pkg-config \
libboost-dev libssl-dev qt515base qt515svg qt515tools zlib1g-dev
- name: Install libtorrent
run: |
git clone --branch v2.0.4 --depth 1 https://github.com/arvidn/libtorrent.git
cd libtorrent
git submodule update --init --recursive
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=Release \
-Ddeprecated-functions=OFF
cmake --build build
sudo cmake --install build
- name: Download Coverity Build Tool
run: |
wget \
-q \
https://scan.coverity.com/download/linux64 \
--post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=qbittorrent%2FqBittorrent" \
-O coverity_tool.tgz
mkdir coverity_tool
tar xzf coverity_tool.tgz --strip 1 -C coverity_tool
- name: Build qBittorrent
run: |
cmake \
-B build \
-G "Ninja" \
-DCMAKE_BUILD_TYPE=Release \
-DGUI=ON \
-DVERBOSE_CONFIGURE=ON
export PATH="$(pwd)/coverity_tool/bin:$PATH"
cov-build --dir cov-int cmake --build build
- name: Submit the result to Coverity Scan
run: |
tar caf qbittorrent.xz cov-int
curl \
--form token="${{ secrets.COVERITY_SCAN_TOKEN }}" \
--form email=sledgehammer999@qbittorrent.org \
--form file=@qbittorrent.xz \
--form version="$(git rev-parse --short HEAD)" \
--form description="master" \
https://scan.coverity.com/builds?project=qbittorrent%2FqBittorrent

View File

@@ -1,85 +0,0 @@
#!/usr/bin/env zsh
set -o nounset
# Assumption: file names don't contain `:` (for the `cut` invocation).
# Safe to assume, as such a character in a filename would cause trouble on Windows, a platform we support
# any regression turn this non-zero
regressions=0
# exclusions (these are just grep extended regular expressions to match against paths relative to the root of the repository)
exclusions_nonutf8='(.*(7z|gif|ic(ns|o)|png|qm|zip))'
exclusions_bom='src/base/unicodestrings.h'
exclusions_tw='(*.ts)|src/webui/www/private/scripts/lib/mootools-1.2-more.js'
exclusions_trailing_newline='configure'
exclusions_no_lf='(*.ts)|(.*svg)|compile_commands.json|src/webui/www/private/scripts/lib/mootools-1.2-(core-yc.js|more.js)'
echo -e "\n*** Detect files not encoded in UTF-8 ***\n"
find . -path ./build -prune -false -o -path ./.git -prune -false -o -type f -exec file --mime {} \; | sort \
| grep -v -e "charset=us-ascii" -e "charset=utf-8" | cut -d ":" -f 1 \
| grep -E -v -e "${exclusions_nonutf8}" \
| tee >(echo -e "--> Files not encoded in UTF-8: found" "$(wc -l < /dev/stdin)" "regression(s)\n") \
| xargs -I my_input -0 bash -c 'echo "my_input"; test "$(echo -n "my_input" | wc -l)" -eq 0'
regressions=$((regressions+$?))
echo -e "\n*** Detect files encoded in UTF-8 with BOM ***\n"
grep --exclude-dir={.git,build} -rIl $'\xEF\xBB\xBF' | sort \
| grep -E -v -e "${exclusions_bom}" \
| tee >(echo -e "--> Files encoded in UTF-8 with BOM: found" "$(wc -l < /dev/stdin)" "regression(s)\n") \
| xargs -I my_input -0 bash -c 'echo "my_input"; test "$(echo -n "my_input" | wc -l)" -eq 0'
regressions=$((regressions+$?))
echo -e "\n*** Detect usage of CR byte ***\n"
grep --exclude-dir={.git,build} -rIlU $'\x0D' | sort \
| tee >(echo -e "--> Usage of CR byte: found" "$(wc -l < /dev/stdin)" "regression(s)\n") \
| xargs -I my_input -0 bash -c 'echo "my_input"; test "$(echo -n "my_input" | wc -l)" -eq 0'
regressions=$((regressions+$?))
echo -e "\n*** Detect trailing whitespace in lines ***\n"
grep --exclude-dir={.git,build} -rIl "[[:blank:]]$" | sort \
| grep -E -v -e "${exclusions_tw}" \
| tee >(echo -e "--> Trailing whitespace in lines: found" "$(wc -l < /dev/stdin)" "regression(s)\n") \
| xargs -I my_input -0 bash -c 'echo "my_input"; test "$(echo -n "my_input" | wc -l)" -eq 0';
regressions=$((regressions+$?))
echo -e "\n*** Detect too many trailing newlines ***\n"
find . -path ./build -prune -false -o -path ./.git -prune -false -o -type f -exec file --mime {} \; | sort \
| grep -e "charset=us-ascii" -e "charset=utf-8" | cut -d ":" -f 1 \
| grep -E -v -e "${exclusions_trailing_newline}" \
| xargs -L1 -I my_input bash -c 'test "$(tail -q -c2 "my_input" | hexdump -C | grep "0a 0a")" && echo "my_input"' \
| tee >(echo -e "--> Too many trailing newlines: found" "$(wc -l < /dev/stdin)" "regression(s)\n") \
| xargs -I my_input -0 bash -c 'echo "my_input"; test "$(echo -n "my_input" | wc -l)" -eq 0'
regressions=$((regressions+$?))
echo -e "\n*** Detect no trailing newline ***\n"
find . -path ./build -prune -false -o -path ./.git -prune -false -o -type f -exec file --mime {} \; | sort \
| grep -e "charset=us-ascii" -e "charset=utf-8" | cut -d ":" -f 1 \
| grep -E -v -e "${exclusions_no_lf}" \
| xargs -L1 -I my_input bash -c 'test "$(tail -q -c1 "my_input" | hexdump -C | grep "0a")" || echo "my_input"' \
| tee >(echo -e "--> No trailing newline: found" "$(wc -l < /dev/stdin)" "regression(s)\n") \
| xargs -I my_input -0 bash -c 'echo "my_input"; test "$(echo -n "my_input" | wc -l)" -eq 0'
regressions=$((regressions+$?))
echo -e "\n*** Detect translation closing tag in new line ***\n"
grep --exclude-dir={.git,build} -nri "^</translation>" | sort \
| cut -d ":" -f 1,2 \
| tee >(echo -e "--> Translation closing tag in new line: found" "$(wc -l < /dev/stdin)" "regression(s)\n") \
| xargs -I my_input -0 bash -c 'echo "my_input"; test "$(echo -n "my_input" | wc -l)" -eq 0'
regressions=$((regressions+$?))
if [ "$regressions" -ne 0 ]; then
regressions=1
echo "\nFile health regressions found. Please fix them (or add them as exclusions)."
else
echo "All OK, no file health regressions found."
fi
exit $regressions;

View File

@@ -1,22 +0,0 @@
name: GitHub Actions file health check
on: [pull_request, push]
jobs:
check_file_health:
name: Check file health
runs-on: ubuntu-20.04
steps:
- name: checkout repository
uses: actions/checkout@v2
- name: install zsh
run: |
sudo apt update
sudo apt install zsh
- name: run check file health script
run: |
./.github/workflows/file_health.sh

22
.github/workflows/stale_bot.yaml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Stale bot
on:
schedule:
- cron: '0 0 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- name: Mark and close stale PRs
uses: actions/stale@v4
with:
stale-pr-message: "This PR is stale because it has been 60 days with no activity. This PR will be automatically closed within 7 days if there is no further activity."
close-pr-message: "This PR was closed because it has been stalled for some time with no activity."
days-before-stale: -1 # avoid marking issues
days-before-pr-stale: 60
days-before-close: -1 # avoid closing issues
days-before-pr-close: 7
exempt-all-pr-assignees: true # avoid stale for all PR with assignees
exempt-all-pr-milestones: true # avoid stale for all PR with milestones
operations-per-run: 200

View File

@@ -1,31 +0,0 @@
name: WebUI CI
on: [pull_request, push]
jobs:
check_webui:
name: Check WebUI
runs-on: ubuntu-20.04
defaults:
run:
working-directory: src/webui/www
steps:
- name: checkout repository
uses: actions/checkout@v2
- name: setup nodejs
uses: actions/setup-node@v2
with:
node-version: '14'
- name: install tools
run: npm install
- name: lint code
run: npm run lint
- name: format code
run: |
npm run format
git diff --exit-code

55
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,55 @@
repos:
- repo: local
hooks:
- id: check-translation-tag
name: Check newline characters in <translation> tag
entry: .github/workflows/check_translation_tag.py
language: script
types_or:
- ts
- repo: https://github.com/pre-commit/pre-commit-hooks.git
rev: v4.0.1
hooks:
- id: check-json
name: Check JSON files
- id: check-yaml
name: Check YAML files
- id: fix-byte-order-marker
name: Check file encoding (UTF-8 without BOM)
exclude: |
(?x)^(
src/base/unicodestrings.h
)$
- id: mixed-line-ending
name: Check line ending character (LF)
args: ["--fix=lf"]
exclude: |
(?x)^(
compile_commands.json |
src/webui/www/private/scripts/lib/.*
)$
- id: end-of-file-fixer
name: Check trailing newlines
exclude: |
(?x)^(
compile_commands.json |
configure |
src/webui/www/private/scripts/lib/.*
)$
exclude_types:
- svg
- ts
- id: trailing-whitespace
name: Check trailing whitespaces
exclude: |
(?x)^(
src/webui/www/private/scripts/lib/.*
)$
exclude_types:
- ts

View File

@@ -1,194 +0,0 @@
language: cpp
os:
- linux
- osx
dist: focal
osx_image: xcode12.2
env:
matrix:
- libt_branch=RC_1_2 gui=true build_system=qmake
- libt_branch=RC_1_2 gui=false build_system=qmake
- libt_branch=RC_1_2 gui=true build_system=cmake
- libt_branch=RC_1_2 gui=false build_system=cmake
global:
- secure: "OI9CUjj4lTb0HwwIZU5PbECU3hLlAL6KC8KsbwohG8/O3j5fLcnmDsK4Ad9us5cC39sS11Jcd1kDP2qRcCuST/glVNhLkcjKkiQerOfd5nQ/qL4JYfz/1mfP5mdpz9jHKzpLUIG+TXkbSTjP6VVmsb5KPT+3pKEdRFZB+Pu9+J8="
- coverity_branch: coverity_scan
jobs:
include:
- env: libt_branch=RC_2_0 gui=true build_system=qmake
os: linux
notifications:
email:
on_success: change
on_failure: change
cache:
ccache: true
directories:
- $HOME/travis/deb
- $HOME/travis/brew
addons:
coverity_scan:
project:
name: "qbittorrent/qBittorrent"
description: "Build submitted via Travis CI"
build_command_prepend: "./bootstrap.sh && ./configure $qmake_conf"
build_command: "make -j2"
branch_pattern: $coverity_branch
notification_email: sledgehammer999@qbittorrent.org
apt:
sources:
# sources list: https://github.com/travis-ci/apt-source-safelist/blob/master/ubuntu.json
- sourceline: 'deb https://apt.kitware.com/ubuntu/ focal main'
key_url: 'https://apt.kitware.com/keys/kitware-archive-latest.asc'
- sourceline: 'ppa:beineri/opt-qt-5.14.2-focal'
packages:
# packages list: https://github.com/travis-ci/apt-package-safelist/blob/master/ubuntu-trusty
- [autoconf, automake, cmake, colormake]
- [libboost-dev, libboost-system-dev]
- libssl-dev
- [qt514base, qt514svg, qt514tools]
- zlib1g-dev
before_install:
# only allow specific build for coverity scan, others will stop
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ] && ! [ "$TRAVIS_OS_NAME" = "linux" -a "$libt_branch" = "RC_1_2" -a "$gui" = "true" -a "$build_system" = "qmake" ]; then exit ; fi
- shopt -s expand_aliases
- alias make="colormake -j2" # Using nprocs/2 sometimes may fail (gcc is killed by system)
- qbt_path="$HOME/qbt_install"
- qmake_conf="$qmake_conf --prefix=$qbt_path"
- cmake_conf="$cmake_conf -DCMAKE_INSTALL_PREFIX=$qbt_path"
# options for specific branches
- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
# setup virtual display for after_success target
if [ "$gui" = "true" ]; then export "DISPLAY=:99.0" && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 ; fi ;
# CMake from Kitware is installed in /usr/bin
# TravisCI installs its own cmake to another location which ovverides other installations
# if they don't call the new binary directly
alias cmake="/usr/bin/cmake"
# Qt 5.14.2
PATH="/opt/qt514/bin:$PATH"
qmake_conf="$qmake_conf PKG_CONFIG_PATH=/opt/qt514/lib/pkgconfig:$PKG_CONFIG_PATH"
cmake_conf="$cmake_conf PKG_CONFIG_PATH=/opt/qt514/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedefs"
openssl_root_path="/usr/local/opt/openssl"
qmake_conf="$qmake_conf PKG_CONFIG_PATH=$openssl_root_path/lib/pkgconfig:$PKG_CONFIG_PATH"
cmake_conf="$cmake_conf -DOPENSSL_ROOT_DIR=$openssl_root_path"
fi
- |
if [ "$gui" = "false" ]; then
qmake_conf="$qmake_conf --disable-gui"
cmake_conf="$cmake_conf -DGUI=OFF"
fi
# print settings
- echo $libt_branch
- echo $gui
- echo $build_system
- echo $qmake_conf
- echo $cmake_conf
install:
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
# dependencies
PATH="/usr/local/opt/ccache/libexec:$PATH"
brew update > /dev/null
brew upgrade cmake
brew install ccache colormake boost openssl qt@5 zlib
brew link --force qt@5 zlib
if [ "$build_system" = "cmake" ]; then
sudo ln -s /usr/local/opt/qt/mkspecs /usr/local/mkspecs
sudo ln -s /usr/local/opt/qt/plugins /usr/local/plugins
fi
fi
- |
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
export use_ccache=true
ccache -M 512M
ccache -V && ccache --show-stats && ccache --zero-stats
fi
- |
if [ "$libt_branch" = "RC_1_2" ]; then
pushd "$HOME"
git clone --single-branch --branch RC_1_2 https://github.com/arvidn/libtorrent.git
cd libtorrent
git checkout tags/v1.2.13
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_STANDARD=17 \
-Ddeprecated-functions=OFF \
-DOPENSSL_ROOT_DIR="$openssl_root_path" \
./
make
sudo make install
popd
elif [ "$libt_branch" = "RC_2_0" ]; then
pushd "$HOME"
git clone --single-branch --branch RC_2_0 https://github.com/arvidn/libtorrent.git
cd libtorrent
git checkout tags/v2.0.3
git submodule update --init --recursive
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CXX_STANDARD=17 \
-Ddeprecated-functions=OFF \
-DOPENSSL_ROOT_DIR="$openssl_root_path" \
./
make
sudo make install
popd
fi
script:
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ]; then exit ; fi # skip usual build when running coverity scan
- |
cd "$TRAVIS_BUILD_DIR"
if [ "$build_system" = "qmake" ]; then
# scan only as lupdate is prone to hang
lupdate -extensions c,cpp,h,hpp,ui ./
./bootstrap.sh
./configure $qmake_conf CXXFLAGS="$CXXFLAGS"
else
mkdir build && cd build
cmake $cmake_conf ../
fi
- make
- make install
after_success:
- if [ "$gui" = "true" ]; then qbt_exe="qbittorrent" ; else qbt_exe="qbittorrent-nox" ; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd "$qbt_path/bin" ; fi
- |
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
if [ "$build_system" = "qmake" ]; then
macdeployqt "$TRAVIS_BUILD_DIR/src/$qbt_exe.app"
cd "$TRAVIS_BUILD_DIR/src/$qbt_exe.app/Contents/MacOS"
else
cd "$qbt_path/$qbt_exe.app/Contents/MacOS"
fi
fi
- ./$qbt_exe --version
after_script:
- if [ "$use_ccache" = true ]; then ccache --show-stats ; fi

View File

@@ -10,17 +10,20 @@ project(qBittorrent
# use CONFIG mode first in find_package
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
# version requirements - older vesions may work, but you are on your own
# version requirements - older versions may work, but you are on your own
set(minBoostVersion 1.65)
set(minQtVersion 5.14)
set(minQt5Version 5.15.2)
set(minQt6Version 6.2)
set(minOpenSSLVersion 1.1.1)
set(minLibtorrentVersion 1.2.13)
set(minLibtorrent1Version 1.2.14)
set(minLibtorrentVersion 2.0.4)
set(minZlibVersion 1.2.11)
# features (some are platform-specific)
include(CheckCXXSourceCompiles) # TODO: migrate to CheckSourceCompiles in CMake >= 3.19
include(FeatureSummary)
include(FeatureOptionsSetup)
feature_option(QT6 "Use Qt6" OFF)
feature_option(STACKTRACE "Enable stacktraces" ON)
feature_option(GUI "Build GUI application" ON)
feature_option(WEBUI "Enables built-in HTTP server for headless use" ON)

View File

@@ -6,22 +6,14 @@ Read the respective section to find out more.
### Table Of Contents
* **[Bug reporting etiquette](#bug-reporting-etiquette)**
* **[Submitting an issue/bug report](#submitting-an-issuebug-report)**
* [What is an actual bug report?](#what-is-an-actual-bug-report)
* [Before submitting a bug report](#before-submitting-a-bug-report)
* [Steps to ensure a good bug report](#steps-to-ensure-a-good-bug-report)
* **[Suggesting enhancements/feature requests](#suggesting-enhancementsfeature-requests)**
* [Before submitting an enhancement/feature request](#before-submitting-an-enhancementfeature-request)
* [Steps to ensure a good enhancement/feature suggestion](#steps-to-ensure-a-good-enhancementfeature-suggestion)
* **[Opening a pull request](#opening-a-pull-request)**
* [Must read](#must-read)
* [Good to know](#good-to-know)
# Bug reporting etiquette
@@ -194,28 +186,26 @@ Following these guidelines helps maintainers and the community understand your s
# Opening a pull request
### Must read
* Read our [**coding guidelines**][coding-guidelines-url]. There are some scripts to help you: [uncrustify script][uncrustify-script-url], [astyle script][astyle-script-url], [(related thread)][coding-guidelines-thread-url].
* Keep the title **short** and provide a **clear** description about what your pull request does.
* Provide **screenshots** for UI related changes.
* Keep your git commit history **clean** and **precise.** Refer to the section about "Git commit messages" in the [**coding guidelines**][coding-guidelines-url].
* If your commit fixes a reported issue (for example #4134), add the following message to the commit `Closes #4134.`. Example [here][commit-message-fix-issue-example-url].
* Consult [coding guidelines][coding-guidelines-url] first. If you are working on translation/i18n, read ["How to translate qBittorrent"][how-to-translate-url].
* Keep your git commit history clean.
* Refer to the section about ["Git commit messages"][coding-guidelines-git-commit-message-url] in the coding guidelines.
* When merge conflicts arise, do `git rebase <target_branch_name>` and fix the conflicts, don't do `git pull`. Here is a good explanation: [merging-vs-rebasing][merging-vs-rebasing-url].
* Keep pull request title concise and provide motivation and "what it does" in the pull request description area. Make it easy to read and understand.
* Provide screenshots for UI related changes.
* If your commit addresses a reported issue (for example issue #8454), append the following text to the commit body `Closes #8454.`. Example [commit][commit-message-fix-issue-example-url].
* Search [pull request list][pull-request-list-url] first. Others might have already implemented your idea (or got rejected already).
### Good to know
* **Search** pull request history! Others might have already implemented your idea and it is waiting to be merged (or got rejected already). Save your precious time by doing a search first.
* When resolving merge conflicts, do `git rebase <target_branch_name>`, don't do `git pull`. Then you can start fixing the conflicts. Here is a good explanation: [link][merging-vs-rebasing-url].
[astyle-script-url]: https://gist.github.com/Chocobo1/539cee860d1eef0acfa6
[attachments-howto-url]: https://help.github.com/articles/file-attachments-on-issues-and-pull-requests
[builds-url]: https://sourceforge.net/projects/qbittorrent/files/
[coding-guidelines-url]: https://github.com/qbittorrent/qBittorrent/blob/master/CODING_GUIDELINES.md
[coding-guidelines-thread-url]: https://github.com/qbittorrent/qBittorrent/issues/2192
[coding-guidelines-git-commit-message-url]: https://github.com/qbittorrent/qBittorrent/blob/master/CODING_GUIDELINES.md#10-git-commit-message
[commit-message-fix-issue-example-url]: https://github.com/qbittorrent/qBittorrent/commit/c07cd440cd46345297debb47cb260f8688975f50
[forum-url]: http://forum.qbittorrent.org/
[howto-report-bugs-url]: https://www.chiark.greenend.org.uk/~sgtatham/bugs.html
[how-to-translate-url]: https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent
[merging-vs-rebasing-url]: https://www.atlassian.com/git/tutorials/merging-vs-rebasing
[pull-request-list-url]: https://github.com/qbittorrent/qBittorrent/pulls
[python-url]: https://www.python.org/
[releases-url]: https://github.com/qbittorrent/qBittorrent/releases
[search-plugins-url]: https://github.com/qbittorrent/search-plugins
[uncrustify-script-url]: https://raw.githubusercontent.com/qbittorrent/qBittorrent/master/uncrustify.cfg
[wiki-url]: https://github.com/qbittorrent/qBittorrent/wiki
[builds-url]: https://sourceforge.net/projects/qbittorrent/files/

65
COPYING
View File

@@ -18,8 +18,8 @@ See also the AUTHORS file
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -32,7 +32,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
@@ -295,3 +295,62 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

14
INSTALL
View File

@@ -5,20 +5,22 @@ qBittorrent - A BitTorrent client in C++ / Qt
- Boost >= 1.65
- libtorrent-rasterbar >= 1.2.13 (by Arvid Norberg)
* https://www.libtorrent.org/
- libtorrent-rasterbar 1.2.14 - 1.2.x || 2.0.4 - 2.0.x
* By Arvid Norberg, https://www.libtorrent.org/
* Be careful: another library (the one used by rTorrent) uses a similar name
- OpenSSL >= 1.1.1
- Qt >= 5.14
- Qt 5.15.2 - 5.x
- zlib >= 1.2.11
- pkg-config (compile-time only on *nix systems)
- pkg-config *
* Compile-time only on *nix systems
- Python >= 3.5.0 (optional, runtime only)
* Required by the internal search engine
- Python >= 3.5.0
* Optional, run-time only
* Used by the bundled search engine
Dependency version numbers are bumped every once in a while to keep the range of properly tested configurations manageable, even if not strictly required to build.
You may be able to build with older versions of (some of) the dependencies other than the minimum versions specified in the build scripts, but support for such builds is not provided - you are on your own.

View File

@@ -1,7 +1,6 @@
qBittorrent - A BitTorrent client in Qt
------------------------------------------
[![TravisCI Status](https://travis-ci.org/qbittorrent/qBittorrent.svg?branch=master)](https://travis-ci.org/qbittorrent/qBittorrent)
[![AppVeyor Status](https://ci.appveyor.com/api/projects/status/github/qbittorrent/qBittorrent?branch=master&svg=true)](https://ci.appveyor.com/project/qbittorrent/qBittorrent)
[![GitHub Actions CI Status](https://github.com/qbittorrent/qBittorrent/workflows/GitHub%20Actions%20CI/badge.svg)](https://github.com/qbittorrent/qBittorrent/actions)
[![Coverity Status](https://scan.coverity.com/projects/5494/badge.svg)](https://scan.coverity.com/projects/5494)
@@ -46,8 +45,8 @@ http://forum.qbittorrent.org
Please report any bug (or feature request) to:
http://bugs.qbittorrent.org
You can also meet me (sledgehammer_999) on IRC:
`#qbittorrent on irc.freenode.net`
Official IRC channel:
`#qbittorrent on irc.libera.chat`
------------------------------------------
sledgehammer999 <sledgehammer999@qbittorrent.org>

View File

@@ -17,7 +17,7 @@ macro(qbt_common_config)
)
target_compile_definitions(qbt_common_cfg INTERFACE
QT_DISABLE_DEPRECATED_BEFORE=0x050e00
QT_DISABLE_DEPRECATED_BEFORE=0x050f02
QT_NO_CAST_TO_ASCII
QT_NO_CAST_FROM_BYTEARRAY
QT_USE_QSTRINGBUILDER
@@ -90,4 +90,7 @@ macro(qbt_common_config)
)
endif()
if (LibtorrentRasterbar_VERSION VERSION_GREATER_EQUAL ${minLibtorrentVersion})
target_compile_definitions(qbt_common_cfg INTERFACE QBT_USES_LIBTORRENT2)
endif()
endmacro(qbt_common_config)

View File

@@ -45,6 +45,9 @@ DEFINES += BOOST_SYSTEM_STATIC_LINK
# Enable if linking dynamically against libtorrent
#DEFINES += TORRENT_LINKING_SHARED
# Enable this if compiling with libtorrent 2.x
#DEFINES += QBT_USES_LIBTORRENT2
# Enable stack trace support
CONFIG += stacktrace

302
configure vendored
View File

@@ -1450,7 +1450,7 @@ Some influential environment variables:
directories to add to pkg-config's search path
PKG_CONFIG_LIBDIR
path overriding pkg-config's built-in search path
QT_QMAKE value of host_bins for Qt5Core >= 5.14, overriding pkg-config
QT_QMAKE value of host_bins for Qt5Core >= 5.15.2, overriding pkg-config
Qt5Svg_CFLAGS
C compiler flags for Qt5Svg, overriding pkg-config
Qt5Svg_LIBS linker flags for Qt5Svg, overriding pkg-config
@@ -5188,29 +5188,38 @@ fi
# Detect OS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether OS is FreeBSD" >&5
printf %s "checking whether OS is FreeBSD... " >&6; }
if expr "$host_os" : ".*freebsd.*" > /dev/null
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to enable specific tweaks for current host \"$host_os\"" >&5
printf %s "checking whether to enable specific tweaks for current host \"$host_os\"... " >&6; }
case "$host_os" in
*darwin*)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
LIBS="-lexecinfo $LIBS"
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
fi
enable_qt_dbus=no
;;
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether OS is macOS" >&5
printf %s "checking whether OS is macOS... " >&6; }
if expr "$host_os" : ".*darwin.*" > /dev/null
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
*freebsd*)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
enable_qt_dbus=no
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
LIBS="-lexecinfo $LIBS"
;;
*haiku*)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
LIBS="-lnetwork $LIBS"
;;
*openbsd*)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
LIBS="-lexecinfo $LIBS"
;;
*)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
fi
;;
esac
# Require 0.23 pkg-config
@@ -5456,8 +5465,8 @@ printf "%s\n" "$enable_webui" >&6; }
esac
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.14\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.14") 2>&5
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.15.2\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.15.2") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
@@ -5466,12 +5475,12 @@ if test -n "$QT_QMAKE"; then
pkg_cv_QT_QMAKE="$QT_QMAKE"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.14\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.14") 2>&5
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.15.2\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.15.2") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_QT_QMAKE=`$PKG_CONFIG --variable="host_bins" "Qt5Core >= 5.14" 2>/dev/null`
pkg_cv_QT_QMAKE=`$PKG_CONFIG --variable="host_bins" "Qt5Core >= 5.15.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -5501,8 +5510,8 @@ fi
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Qt5 qmake >= 5.14" >&5
printf %s "checking for Qt5 qmake >= 5.14... " >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Qt5 qmake >= 5.15.2" >&5
printf %s "checking for Qt5 qmake >= 5.15.2... " >&6; }
if test "x$QT_QMAKE" != "x"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $QT_QMAKE" >&5
@@ -5522,19 +5531,19 @@ if test "x$enable_gui" = "xyes"
then :
pkg_failed=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Qt5Svg" >&5
printf %s "checking for Qt5Svg... " >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Qt5Svg >= 5.15.2" >&5
printf %s "checking for Qt5Svg >= 5.15.2... " >&6; }
if test -n "$Qt5Svg_CFLAGS"; then
pkg_cv_Qt5Svg_CFLAGS="$Qt5Svg_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Svg >= 5.14\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Svg >= 5.14") 2>&5
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Svg >= 5.15.2\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Svg >= 5.15.2") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_Qt5Svg_CFLAGS=`$PKG_CONFIG --cflags "Qt5Svg >= 5.14" 2>/dev/null`
pkg_cv_Qt5Svg_CFLAGS=`$PKG_CONFIG --cflags "Qt5Svg >= 5.15.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -5546,12 +5555,12 @@ if test -n "$Qt5Svg_LIBS"; then
pkg_cv_Qt5Svg_LIBS="$Qt5Svg_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Svg >= 5.14\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Svg >= 5.14") 2>&5
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Svg >= 5.15.2\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Svg >= 5.15.2") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_Qt5Svg_LIBS=`$PKG_CONFIG --libs "Qt5Svg >= 5.14" 2>/dev/null`
pkg_cv_Qt5Svg_LIBS=`$PKG_CONFIG --libs "Qt5Svg >= 5.15.2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -5563,7 +5572,7 @@ fi
if test $pkg_failed = yes; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -5572,14 +5581,14 @@ else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
Qt5Svg_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "Qt5Svg >= 5.14" 2>&1`
Qt5Svg_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "Qt5Svg >= 5.15.2" 2>&1`
else
Qt5Svg_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "Qt5Svg >= 5.14" 2>&1`
Qt5Svg_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "Qt5Svg >= 5.15.2" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$Qt5Svg_PKG_ERRORS" >&5
as_fn_error $? "Package requirements (Qt5Svg >= 5.14) were not met:
as_fn_error $? "Package requirements (Qt5Svg >= 5.15.2) were not met:
$Qt5Svg_PKG_ERRORS
@@ -5590,7 +5599,7 @@ Alternatively, you may set the environment variables Qt5Svg_CFLAGS
and Qt5Svg_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -5619,11 +5628,11 @@ case "x$enable_qt_dbus" in #(
"xyes") :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Qt5DBus >= 5.14" >&5
printf %s "checking for Qt5DBus >= 5.14... " >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for Qt5DBus >= 5.15.2" >&5
printf %s "checking for Qt5DBus >= 5.15.2... " >&6; }
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5DBus >= 5.14\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5DBus >= 5.14") 2>&5
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5DBus >= 5.15.2\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5DBus >= 5.15.2") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
@@ -6032,19 +6041,19 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
pkg_failed=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent" >&5
printf %s "checking for libtorrent... " >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 2.0.4" >&5
printf %s "checking for libtorrent-rasterbar >= 2.0.4... " >&6; }
if test -n "$libtorrent_CFLAGS"; then
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.13\""; } >&5
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.13") 2>&5
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 2.0.4\""; } >&5
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 2.0.4") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.2.13" 2>/dev/null`
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 2.0.4" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -6056,12 +6065,12 @@ if test -n "$libtorrent_LIBS"; then
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.13\""; } >&5
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.13") 2>&5
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 2.0.4\""; } >&5
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 2.0.4") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.2.13" 2>/dev/null`
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 2.0.4" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -6073,7 +6082,7 @@ fi
if test $pkg_failed = yes; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -6082,14 +6091,73 @@ else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.13" 2>&1`
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 2.0.4" 2>&1`
else
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.13" 2>&1`
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 2.0.4" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$libtorrent_PKG_ERRORS" >&5
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.2.13) were not met:
pkg_failed=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2" >&5
printf %s "checking for libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2... " >&6; }
if test -n "$libtorrent_CFLAGS"; then
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2\""; } >&5
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test -n "$libtorrent_LIBS"; then
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2\""; } >&5
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2" 2>&1`
else
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$libtorrent_PKG_ERRORS" >&5
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2) were not met:
$libtorrent_PKG_ERRORS
@@ -6100,7 +6168,7 @@ Alternatively, you may set the environment variables libtorrent_CFLAGS
and libtorrent_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -6119,14 +6187,114 @@ else
libtorrent_LIBS=$pkg_cv_libtorrent_LIBS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS"
LIBS="$libtorrent_LIBS $LIBS"
CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS" LIBS="$libtorrent_LIBS $LIBS"
fi
elif test $pkg_failed = untried; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
pkg_failed=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2" >&5
printf %s "checking for libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2... " >&6; }
if test -n "$libtorrent_CFLAGS"; then
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2\""; } >&5
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test -n "$libtorrent_LIBS"; then
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
elif test -n "$PKG_CONFIG"; then
if test -n "$PKG_CONFIG" && \
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2\""; } >&5
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2" 2>&1`
else
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$libtorrent_PKG_ERRORS" >&5
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2) were not met:
$libtorrent_PKG_ERRORS
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
Alternatively, you may set the environment variables libtorrent_CFLAGS
and libtorrent_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
Alternatively, you may set the environment variables libtorrent_CFLAGS
and libtorrent_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
See \`config.log' for more details" "$LINENO" 5; }
else
libtorrent_CFLAGS=$pkg_cv_libtorrent_CFLAGS
libtorrent_LIBS=$pkg_cv_libtorrent_LIBS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS" LIBS="$libtorrent_LIBS $LIBS"
fi
else
libtorrent_CFLAGS=$pkg_cv_libtorrent_CFLAGS
libtorrent_LIBS=$pkg_cv_libtorrent_LIBS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS" LIBS="$libtorrent_LIBS $LIBS" QBT_ADD_DEFINES="$QBT_ADD_DEFINES QBT_USES_LIBTORRENT2"
fi
pkg_failed=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for openssl" >&5
printf %s "checking for openssl... " >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for openssl >= 1.1.1" >&5
printf %s "checking for openssl >= 1.1.1... " >&6; }
if test -n "$openssl_CFLAGS"; then
pkg_cv_openssl_CFLAGS="$openssl_CFLAGS"
@@ -6166,7 +6334,7 @@ fi
if test $pkg_failed = yes; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -6193,7 +6361,7 @@ Alternatively, you may set the environment variables openssl_CFLAGS
and openssl_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
@@ -6218,8 +6386,8 @@ fi
pkg_failed=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib" >&5
printf %s "checking for zlib... " >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib >= 1.2.11" >&5
printf %s "checking for zlib >= 1.2.11... " >&6; }
if test -n "$zlib_CFLAGS"; then
pkg_cv_zlib_CFLAGS="$zlib_CFLAGS"
@@ -6259,7 +6427,7 @@ fi
if test $pkg_failed = yes; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -6286,7 +6454,7 @@ Alternatively, you may set the environment variables zlib_CFLAGS
and zlib_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}

View File

@@ -1,3 +1,4 @@
AC_INIT([qbittorrent], [v4.4.0alpha], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
@@ -53,17 +54,32 @@ AC_ARG_ENABLE(qt-dbus,
[enable_qt_dbus=yes])
# Detect OS
AC_MSG_CHECKING([whether OS is FreeBSD])
AS_IF([expr "$host_os" : ".*freebsd.*" > /dev/null],
[AC_MSG_RESULT([yes])
LIBS="-lexecinfo $LIBS"],
[AC_MSG_RESULT([no])])
AC_MSG_CHECKING([whether to enable specific tweaks for current host "$host_os"])
case "$host_os" in
*darwin*)
AC_MSG_RESULT([yes])
enable_qt_dbus=no
;;
AC_MSG_CHECKING([whether OS is macOS])
AS_IF([expr "$host_os" : ".*darwin.*" > /dev/null],
[AC_MSG_RESULT([yes])
enable_qt_dbus=no],
[AC_MSG_RESULT([no])])
*freebsd*)
AC_MSG_RESULT([yes])
LIBS="-lexecinfo $LIBS"
;;
*haiku*)
AC_MSG_RESULT([yes])
LIBS="-lnetwork $LIBS"
;;
*openbsd*)
AC_MSG_RESULT([yes])
LIBS="-lexecinfo $LIBS"
;;
*)
AC_MSG_RESULT([no])
;;
esac
# Require 0.23 pkg-config
PKG_PROG_PKG_CONFIG([0.23])
@@ -141,7 +157,7 @@ AS_IF([test "x$QT_QMAKE" = "x"],
[AC_MSG_ERROR([Could not find qmake])
])
AS_IF([test "x$enable_gui" = "xyes"],
[PKG_CHECK_MODULES(Qt5Svg, [Qt5Svg >= 5.14])
[PKG_CHECK_MODULES(Qt5Svg, [Qt5Svg >= 5.15.2])
])
AC_MSG_CHECKING([whether QtDBus should be enabled])
AS_CASE(["x$enable_qt_dbus"],
@@ -176,9 +192,11 @@ AC_COMPILE_IFELSE([DETECT_BOOST_VERSION_PROGRAM(106000)], [],
[QBT_ADD_DEFINES="$QBT_ADD_DEFINES BOOST_NO_CXX11_RVALUE_REFERENCES"])
PKG_CHECK_MODULES(libtorrent,
[libtorrent-rasterbar >= 1.2.13],
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS"
LIBS="$libtorrent_LIBS $LIBS"])
[libtorrent-rasterbar >= 2.0.4],
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS" LIBS="$libtorrent_LIBS $LIBS" QBT_ADD_DEFINES="$QBT_ADD_DEFINES QBT_USES_LIBTORRENT2"],
[PKG_CHECK_MODULES(libtorrent,
[libtorrent-rasterbar >= 1.2.14 libtorrent-rasterbar < 2],
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS" LIBS="$libtorrent_LIBS $LIBS"])])
PKG_CHECK_MODULES(openssl,
[openssl >= 1.1.1],

View File

@@ -49,8 +49,8 @@ Name[da]=qBittorrent
Comment[de]=Über BitTorrent Dateien herunterladen und teilen
GenericName[de]=BitTorrent Client
Name[de]=qBittorrent
Comment[el]=Κάντε λήψη και ανταλλάξτε αρχεία μέσω BitTorrent
GenericName[el]=Πελάτης BitTorrent
Comment[el]=Κάντε λήψη και μοιραστείτε αρχεία μέσω BitTorrent
GenericName[el]=BitTorrent client
Name[el]=qBittorrent
Comment[en_GB]=Download and share files over BitTorrent
GenericName[en_GB]=BitTorrent client
@@ -70,7 +70,7 @@ Name[fa]=کیو بیت تورنت
Comment[fi]=Lataa ja jaa tiedostoja BitTorrentia käyttäen
GenericName[fi]=BitTorrent-asiakasohjelma
Name[fi]=qBittorrent
Comment[fr]=Télécharger et partager des fichiers avec BitTorrent
Comment[fr]=Letölteni és megosztani a dokumentumokat Bittorrenttel
GenericName[fr]=Client BitTorrent
Name[fr]=qBittorrent
Comment[gl]=Descargar e compartir ficheiros co protocolo BitTorrent
@@ -106,8 +106,8 @@ Name[ja]=qBittorrent
Comment[ka]=ჩამოტვირთე და გააზიარე ფაილები Bittorrent-ის საშუალებით
GenericName[ka]=BitTorrent კლიენტი
Name[ka]=qBittorrent
Comment[ko]=비트토렌트를 통해 파일을 받고 공유합니다
GenericName[ko]=비트토렌트 클라이언트
Comment[ko]=BitTorrent를 통해 파일 다운로드 및 공유
GenericName[ko]=BitTorrent 클라이언트
Name[ko]=qBittorrent
Comment[zh]=通过 BitTorrent 下载和分享文件
GenericName[zh]=BitTorrent 客户端
@@ -153,6 +153,7 @@ GenericName[sl]=BitTorrent odjemalec
Name[sl]=qBittorrent
Comment[sr]=Преузимајте и делите фајлове преко BitTorrent протокола
GenericName[sr]=BitTorrent-клијент
Name[sr]=qBittorrent
Comment[sr@latin]=Preuzimanje i deljenje fajlova preko BitTorrent-a
GenericName[sr@latin]=BitTorrent klijent
Name[sr@latin]=qBittorrent
@@ -177,8 +178,8 @@ Name[ur]=قیو بٹ ٹورنٹ
Comment[uk]=Завантажуйте та поширюйте файли через BitTorrent
GenericName[uk]=BitTorrent-клієнт
Name[uk]=qBittorrent
Comment[vi]=Tải xuống và chia sẻ tệp qua BitTorrent
GenericName[vi]=Client BitTorrent
Comment[vi]=Tải về và chia sẻ tệp qua BitTorrent
GenericName[vi]=Máy khách BitTorrent
Name[vi]=qBittorrent
Comment[az@latin]=Faylları BitTorrent vasitəsilə göndərin və paylaşın
GenericName[az@latin]=BitTorrent client
@@ -192,6 +193,9 @@ Name[zh_TW]=qBittorrent
Comment[lv_LV]=Lejupielādēt un koplietot failus ar BitTorrent
GenericName[lv_LV]=BitTorrent klients
Name[lv_LV]=qBittorrent
Comment[kk]=BitTorrent арқылы файл жүктеу және бөлісу
GenericName[kk]=BitTorrent клиенті
Name[kk]=qBittorrent
Comment[ms_MY]=Muat turun dan kongsi fail melalui BitTorrent
GenericName[ms_MY]=Klien BitTorrent
Name[ms_MY]=qBittorrent
@@ -213,6 +217,6 @@ Name[te]=క్యు బిట్ టొరెంట్
Comment[pt_PT]=Transferir e partilhar ficheiros por BitTorrent
GenericName[pt_PT]=Cliente BitTorrent
Name[pt_PT]=qBittorrent
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ด้วยบิททอเร้น
GenericName[th]=โปรแกรมบิททอเร้น
Comment[th]=ดาวน์โหลดและแชร์ไฟล์ผ่าน BitTorrent
GenericName[th]=ไคลเอนต์ BitTorrent
Name[th]=qBittorrent

View File

@@ -9,6 +9,7 @@ Type=simple
PrivateTmp=false
User=%i
ExecStart=@EXPAND_BINDIR@/qbittorrent-nox
TimeoutStopSec=1800
[Install]
WantedBy=multi-user.target

View File

@@ -7,7 +7,7 @@ LangString inst_dekstop ${LANG_DANISH} "Opret skrivebordsgenvej"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_DANISH} "Opret genvej i menuen Start"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_DANISH} "Start qBittorrent i Windows opstart"
LangString inst_startup ${LANG_DANISH} "Start qBittorrent når Windows starter"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_DANISH} "Åbn .torrent-filer med qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
@@ -15,7 +15,7 @@ LangString inst_magnet ${LANG_DANISH} "Åbn magnet-links med qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_DANISH} "Tilføj Windows Firewall-regel"
;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_DANISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_DANISH} "Deaktivér grænse for for lang Windows-stien kan være (MAX_PATH-begrænsning på 260 tegn, kræver Windows 10 1607 eller senere)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_DANISH} "Tilføjer Windows Firewall-regel"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
@@ -27,11 +27,11 @@ LangString inst_unist ${LANG_DANISH} "Afinstallerer tidligere version."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_DANISH} "Start qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_DANISH} "Dette installationsprogram virker kun i 64-bit Windows versioner."
LangString inst_requires_64bit ${LANG_DANISH} "Installationsprogrammet virker kun i Windows-versioner som er 64-bit."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_DANISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_DANISH} "qBittorrent-versionen kræver mindst Windows 7."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_DANISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_DANISH} "Afinstaller qBittorrent"
;------------------------------------
;Uninstaller strings

View File

@@ -1,60 +1,60 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_INDONESIAN} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_INDONESIAN} "qBittorrent (diperlukan)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_INDONESIAN} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_INDONESIAN} "Buat Pintasan Desktop"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_INDONESIAN} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_INDONESIAN} "Buat Pintasan Menu"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_INDONESIAN} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_INDONESIAN} "Jalankan qBittorrent pada start up Windows"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_INDONESIAN} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_INDONESIAN} "Buka file .torrent dengan qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_INDONESIAN} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_INDONESIAN} "Buka link magnet dengan qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_INDONESIAN} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_INDONESIAN} "Tambahkan aturan Windows Firewall"
;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_INDONESIAN} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_INDONESIAN} "Matikan batasan panjang path Windows (limitasi MAX_PATH 260 karaker, membutuhkan Windows 10 1607 atau lebih baru)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_INDONESIAN} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_INDONESIAN} "Menambahkan aturan Windows Firewall"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_INDONESIAN} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_INDONESIAN} "qBittorrent berjalan. Mohon tutup aplikasi sebelum memasang."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_INDONESIAN} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_INDONESIAN} "Versi saat ini akan dicopot. Pengaturan pengguna dan torrent akan tetap utuh."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_INDONESIAN} "Uninstalling previous version."
LangString inst_unist ${LANG_INDONESIAN} "Mencopot versi sebelumnya."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_INDONESIAN} "Launch qBittorrent."
LangString launch_qbt ${LANG_INDONESIAN} "Buka qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_INDONESIAN} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_INDONESIAN} "Aplikasi ini hanya berjalan pada versi Windows 64-bit."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_INDONESIAN} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_INDONESIAN} "Versi qBittorrent ini membutuhkan setidaknya Windows 7."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_INDONESIAN} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_INDONESIAN} "Copot qBittorrent"
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_INDONESIAN} "Remove files"
LangString remove_files ${LANG_INDONESIAN} "Hapus file"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_INDONESIAN} "Remove shortcuts"
LangString remove_shortcuts ${LANG_INDONESIAN} "Hapus pintasan"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_INDONESIAN} "Remove file associations"
LangString remove_associations ${LANG_INDONESIAN} "Hapus asosiasi file"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_INDONESIAN} "Remove registry keys"
LangString remove_registry ${LANG_INDONESIAN} "Hapus key registri"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_INDONESIAN} "Remove configuration files"
LangString remove_conf ${LANG_INDONESIAN} "Hapus file konfigurasi"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_INDONESIAN} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_INDONESIAN} "Hapus aturan Windows Firewall"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_INDONESIAN} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_INDONESIAN} "Menghapus aturan Windows Firewall"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_INDONESIAN} "Remove torrents and cached data"
LangString remove_cache ${LANG_INDONESIAN} "Hapus torrent dan data ter-cache"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_INDONESIAN} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_INDONESIAN} "qBittorrent berjalan. Mohon tutup aplikasi sebelum mencopot."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_INDONESIAN} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_INDONESIAN} "Tidak menghapus asosiasi .torrent. Terasosiasi dengan:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_INDONESIAN} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_INDONESIAN} "Tidak menghapus asosiasi magnet. Terasosiasi dengan:"

View File

@@ -1,60 +1,60 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_KOREAN} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_KOREAN} "qBittorrent (필요함)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_KOREAN} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_KOREAN} "바탕화면 바로가기 만들기"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_KOREAN} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_KOREAN} "시작 메뉴 바로가기 만들기"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_KOREAN} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_KOREAN} "Windows 시작 시 qBittorrent 시작"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_KOREAN} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_KOREAN} "qBittorrent로 .torrent 파일 열기"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_KOREAN} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_KOREAN} "qBittorrent로 자석 링크 열기"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_KOREAN} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_KOREAN} "Windows 방화벽 규칙 추가"
;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_KOREAN} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_KOREAN} "Windows 경로 길이 제한 비활성화(260자 MAX_PATH 제한, Windows 10 1607 이상 필요)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_KOREAN} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_KOREAN} "Windows 방화벽 규칙 추가하는 중"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_KOREAN} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_KOREAN} "qBittorrent가 실행 중입니다. 설치하기 전에 응용 프로그램을 닫으십시오."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_KOREAN} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_KOREAN} "현재 버전이 삭제됩니다. 사용자 설정과 토렌트는 그대로 유지됩니다."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_KOREAN} "Uninstalling previous version."
LangString inst_unist ${LANG_KOREAN} "이전 버전을 삭제하는 중입니다."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_KOREAN} "Launch qBittorrent."
LangString launch_qbt ${LANG_KOREAN} "qBittorrent를 실행합니다."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_KOREAN} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_KOREAN} "이 설치 프로그램은 64비트 윈도우즈 버전에서만 작동합니다."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_KOREAN} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_KOREAN} " qBittorrent 버전에는 Windows 7 이상이 필요합니다."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_KOREAN} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_KOREAN} "qBittorrent 삭제"
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_KOREAN} "Remove files"
LangString remove_files ${LANG_KOREAN} "파일 제거"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_KOREAN} "Remove shortcuts"
LangString remove_shortcuts ${LANG_KOREAN} "바로가기 제거"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_KOREAN} "Remove file associations"
LangString remove_associations ${LANG_KOREAN} "파일 연결 제거"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_KOREAN} "Remove registry keys"
LangString remove_registry ${LANG_KOREAN} "레지스트리 키 제거"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_KOREAN} "Remove configuration files"
LangString remove_conf ${LANG_KOREAN} "구성 파일 제거"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_KOREAN} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_KOREAN} "Windows 방화벽 규칙 제거"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_KOREAN} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_KOREAN} "Windows 방화벽 규칙 제거하는 중"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_KOREAN} "Remove torrents and cached data"
LangString remove_cache ${LANG_KOREAN} "토렌트 및 캐시된 데이터 제거"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_KOREAN} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_KOREAN} "qBittorrent가 실행 중입니다. 삭제하기 전에 응용 프로그램을 닫으십시오."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_KOREAN} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_KOREAN} ".torrent 연결을 제거하지 않습니다. 관련 항목:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_KOREAN} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_KOREAN} "자석 연결을 제거하지 않습니다. 관련 항목:"

View File

@@ -1,60 +1,60 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_POLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_POLISH} "qBittorrent (wymagany)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_POLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_POLISH} "Utwórz skrót na pulpicie"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_POLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_POLISH} "Utwórz skrót w menu Start"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_POLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_POLISH} "Uruchamiaj qBittorrent wraz z systemem Windows"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_POLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_POLISH} "Otwieraj pliki .torrent za pomocą qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_POLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_POLISH} "Otwieraj odnośniki magnet za pomocą qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_POLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_POLISH} "Dodaj regułę Zapory systemu Windows"
;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_POLISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_POLISH} "Wyłącz ograniczenie długości ścieżki systemu Windows (ograniczenie MAX_PATH do 260 znaków, wymaga systemu Windows 10 1607 lub nowszego)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_POLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_POLISH} "Dodawanie reguły Zapory systemu Windows"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_POLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_POLISH} "qBittorrent jest uruchomiony. Proszę zamknąć aplikację przed instalacją."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_POLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_POLISH} "Obecna wersja zostanie odinstalowana. Ustawienia użytkownika i torrenty pozostaną nienaruszone."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_POLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_POLISH} "Odinstalowywanie poprzedniej wersji."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_POLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_POLISH} "Uruchom qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_POLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_POLISH} "Ten instalator działa tylko w 64-bitowych wersjach systemu Windows."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_POLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_POLISH} "Ta wersja qBittorrent wymaga co najmniej systemu Windows 7."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_POLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_POLISH} "Odinstaluj qBittorrent"
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_POLISH} "Remove files"
LangString remove_files ${LANG_POLISH} "Usuń pliki"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_POLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_POLISH} "Usuń skróty"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_POLISH} "Remove file associations"
LangString remove_associations ${LANG_POLISH} "Usuń skojarzenia plików"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_POLISH} "Remove registry keys"
LangString remove_registry ${LANG_POLISH} "Usuń klucze rejestru"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_POLISH} "Remove configuration files"
LangString remove_conf ${LANG_POLISH} "Usuń pliki konfiguracyjne"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_POLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_POLISH} "Usuń regułę Zapory systemu Windows"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_POLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_POLISH} "Usuwanie reguły Zapory systemu Windows"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_POLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_POLISH} "Usuń torrenty i dane z pamięci podręcznej"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_POLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_POLISH} "qBittorrent jest uruchomiony. Zamknij aplikację przed odinstalowaniem."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_POLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_POLISH} "Bez usuwania skojarzenia .torrent. Skojarzono z:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_POLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_POLISH} "Bez usuwania skojarzenia magnet. Skojarzono z:"

View File

@@ -15,21 +15,21 @@ LangString inst_magnet ${LANG_TRADCHINESE} "使用 qBittorrent 開啟 magnet 連
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_TRADCHINESE} "建立 Windows 防火牆規則"
;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_TRADCHINESE} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
LangString inst_pathlimit ${LANG_TRADCHINESE} "停用 Windows 路徑長度限制 (MAX_PATH 260 字元限制,需 Windows 10 1607 以上版本)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_TRADCHINESE} "正在建立 Windows 防火牆規則"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_TRADCHINESE} "qBittorrent 正在執行中,請先關閉後再進行安裝。"
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_TRADCHINESE} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_TRADCHINESE} "目前版本將被解除安裝。使用者設定及 torrent 將保留。"
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_TRADCHINESE} "正在移除先前版本"
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_TRADCHINESE} "啟動 qBittorrent"
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_TRADCHINESE} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_TRADCHINESE} "此安裝程式僅支援 64 位元版本的 Windows。"
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_TRADCHINESE} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_TRADCHINESE} " qBittorrent 版本僅支援 Windows 7 以上的系統。"
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_TRADCHINESE} "移除 qBittorrent"

215
m4/pkg.m4
View File

@@ -1,29 +1,60 @@
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
# serial 1 (pkg-config-0.24)
#
# Copyright © 2004 Scott James Remnant <scott@netsplit.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
# serial 12 (pkg-config-0.29.2)
# PKG_PROG_PKG_CONFIG([MIN-VERSION])
# ----------------------------------
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation; either version 2 of the License, or
dnl (at your option) any later version.
dnl
dnl This program is distributed in the hope that it will be useful, but
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
dnl General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program; if not, write to the Free Software
dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
dnl 02111-1307, USA.
dnl
dnl As a special exception to the GNU General Public License, if you
dnl distribute this file as part of a program that contains a
dnl configuration script generated by Autoconf, you may include it under
dnl the same distribution terms that you use for the rest of that
dnl program.
dnl PKG_PREREQ(MIN-VERSION)
dnl -----------------------
dnl Since: 0.29
dnl
dnl Verify that the version of the pkg-config macros are at least
dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
dnl installed version of pkg-config, this checks the developer's version
dnl of pkg.m4 when generating configure.
dnl
dnl To ensure that this macro is defined, also add:
dnl m4_ifndef([PKG_PREREQ],
dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
dnl
dnl See the "Since" comment for each macro you use to see what version
dnl of the macros you require.
m4_defun([PKG_PREREQ],
[m4_define([PKG_MACROS_VERSION], [0.29.2])
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
])dnl PKG_PREREQ
dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
dnl ----------------------------------
dnl Since: 0.16
dnl
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
dnl first found in the path. Checks that the version of pkg-config found
dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
dnl used since that's the first version where most current features of
dnl pkg-config existed.
AC_DEFUN([PKG_PROG_PKG_CONFIG],
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
@@ -45,18 +76,19 @@ if test -n "$PKG_CONFIG"; then
PKG_CONFIG=""
fi
fi[]dnl
])# PKG_PROG_PKG_CONFIG
])dnl PKG_PROG_PKG_CONFIG
# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
#
# Check to see whether a particular set of modules exists. Similar
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
#
# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
# only at the first occurence in configure.ac, so if the first place
# it's called might be skipped (such as if it is within an "if", you
# have to call PKG_CHECK_EXISTS manually
# --------------------------------------------------------------
dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl -------------------------------------------------------------------
dnl Since: 0.18
dnl
dnl Check to see whether a particular set of modules exists. Similar to
dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
dnl
dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
dnl only at the first occurence in configure.ac, so if the first place
dnl it's called might be skipped (such as if it is within an "if", you
dnl have to call PKG_CHECK_EXISTS manually
AC_DEFUN([PKG_CHECK_EXISTS],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
if test -n "$PKG_CONFIG" && \
@@ -66,8 +98,10 @@ m4_ifvaln([$3], [else
$3])dnl
fi])
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
# ---------------------------------------------
dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
dnl ---------------------------------------------
dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
dnl pkg_failed based on the result.
m4_define([_PKG_CONFIG],
[if test -n "$$1"; then
pkg_cv_[]$1="$$1"
@@ -79,10 +113,11 @@ m4_define([_PKG_CONFIG],
else
pkg_failed=untried
fi[]dnl
])# _PKG_CONFIG
])dnl _PKG_CONFIG
# _PKG_SHORT_ERRORS_SUPPORTED
# -----------------------------
dnl _PKG_SHORT_ERRORS_SUPPORTED
dnl ---------------------------
dnl Internal check to see if pkg-config supports short errors.
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -90,26 +125,24 @@ if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
else
_pkg_short_errors_supported=no
fi[]dnl
])# _PKG_SHORT_ERRORS_SUPPORTED
])dnl _PKG_SHORT_ERRORS_SUPPORTED
# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
# [ACTION-IF-NOT-FOUND])
#
#
# Note that if there is a possibility the first call to
# PKG_CHECK_MODULES might not happen, you should be sure to include an
# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
#
#
# --------------------------------------------------------------
dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
dnl [ACTION-IF-NOT-FOUND])
dnl --------------------------------------------------------------
dnl Since: 0.4.0
dnl
dnl Note that if there is a possibility the first call to
dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
AC_DEFUN([PKG_CHECK_MODULES],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
pkg_failed=no
AC_MSG_CHECKING([for $1])
AC_MSG_CHECKING([for $2])
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
@@ -119,7 +152,7 @@ and $1[]_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.])
if test $pkg_failed = yes; then
AC_MSG_RESULT([no])
AC_MSG_RESULT([no])
_PKG_SHORT_ERRORS_SUPPORTED
if test $_pkg_short_errors_supported = yes; then
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
@@ -140,7 +173,7 @@ installed software in a non-standard prefix.
_PKG_TEXT])[]dnl
])
elif test $pkg_failed = untried; then
AC_MSG_RESULT([no])
AC_MSG_RESULT([no])
m4_default([$4], [AC_MSG_FAILURE(
[The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
@@ -156,16 +189,40 @@ else
AC_MSG_RESULT([yes])
$3
fi[]dnl
])# PKG_CHECK_MODULES
])dnl PKG_CHECK_MODULES
# PKG_INSTALLDIR(DIRECTORY)
# -------------------------
# Substitutes the variable pkgconfigdir as the location where a module
# should install pkg-config .pc files. By default the directory is
# $libdir/pkgconfig, but the default can be changed by passing
# DIRECTORY. The user can override through the --with-pkgconfigdir
# parameter.
dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
dnl [ACTION-IF-NOT-FOUND])
dnl ---------------------------------------------------------------------
dnl Since: 0.29
dnl
dnl Checks for existence of MODULES and gathers its build flags with
dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
dnl and VARIABLE-PREFIX_LIBS from --libs.
dnl
dnl Note that if there is a possibility the first call to
dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
dnl configure.ac.
AC_DEFUN([PKG_CHECK_MODULES_STATIC],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
_save_PKG_CONFIG=$PKG_CONFIG
PKG_CONFIG="$PKG_CONFIG --static"
PKG_CHECK_MODULES($@)
PKG_CONFIG=$_save_PKG_CONFIG[]dnl
])dnl PKG_CHECK_MODULES_STATIC
dnl PKG_INSTALLDIR([DIRECTORY])
dnl -------------------------
dnl Since: 0.27
dnl
dnl Substitutes the variable pkgconfigdir as the location where a module
dnl should install pkg-config .pc files. By default the directory is
dnl $libdir/pkgconfig, but the default can be changed by passing
dnl DIRECTORY. The user can override through the --with-pkgconfigdir
dnl parameter.
AC_DEFUN([PKG_INSTALLDIR],
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
m4_pushdef([pkg_description],
@@ -176,16 +233,18 @@ AC_ARG_WITH([pkgconfigdir],
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
m4_popdef([pkg_default])
m4_popdef([pkg_description])
]) dnl PKG_INSTALLDIR
])dnl PKG_INSTALLDIR
# PKG_NOARCH_INSTALLDIR(DIRECTORY)
# -------------------------
# Substitutes the variable noarch_pkgconfigdir as the location where a
# module should install arch-independent pkg-config .pc files. By
# default the directory is $datadir/pkgconfig, but the default can be
# changed by passing DIRECTORY. The user can override through the
# --with-noarch-pkgconfigdir parameter.
dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
dnl --------------------------------
dnl Since: 0.27
dnl
dnl Substitutes the variable noarch_pkgconfigdir as the location where a
dnl module should install arch-independent pkg-config .pc files. By
dnl default the directory is $datadir/pkgconfig, but the default can be
dnl changed by passing DIRECTORY. The user can override through the
dnl --with-noarch-pkgconfigdir parameter.
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
m4_pushdef([pkg_description],
@@ -196,13 +255,15 @@ AC_ARG_WITH([noarch-pkgconfigdir],
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
m4_popdef([pkg_default])
m4_popdef([pkg_description])
]) dnl PKG_NOARCH_INSTALLDIR
])dnl PKG_NOARCH_INSTALLDIR
# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
# -------------------------------------------
# Retrieves the value of the pkg-config variable for the given module.
dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl -------------------------------------------
dnl Since: 0.28
dnl
dnl Retrieves the value of the pkg-config variable for the given module.
AC_DEFUN([PKG_CHECK_VAR],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
@@ -211,4 +272,4 @@ _PKG_CONFIG([$1], [variable="][$3]["], [$2])
AS_VAR_COPY([$1], [pkg_cv_][$1])
AS_VAR_IF([$1], [""], [$5], [$4])dnl
])# PKG_CHECK_VAR
])dnl PKG_CHECK_VAR

View File

@@ -5,9 +5,9 @@
# Sets the QT_QMAKE variable to the path of Qt5 qmake if found.
# --------------------------------------
AC_DEFUN([FIND_QT5],
[PKG_CHECK_EXISTS([Qt5Core >= 5.14],
[PKG_CHECK_EXISTS([Qt5Core >= 5.15.2],
[PKG_CHECK_VAR(QT_QMAKE,
[Qt5Core >= 5.14],
[Qt5Core >= 5.15.2],
[host_bins])
])
@@ -18,7 +18,7 @@ AS_IF([test -f "$QT_QMAKE/qmake"],
[QT_QMAKE=""])
])
AC_MSG_CHECKING([for Qt5 qmake >= 5.14])
AC_MSG_CHECKING([for Qt5 qmake >= 5.15.2])
AS_IF([test "x$QT_QMAKE" != "x"],
[AC_MSG_RESULT([$QT_QMAKE])],
[AC_MSG_RESULT([not found])]
@@ -29,8 +29,8 @@ AS_IF([test "x$QT_QMAKE" != "x"],
# Sets the HAVE_QTDBUS variable to true or false.
# --------------------------------------
AC_DEFUN([FIND_QTDBUS],
[AC_MSG_CHECKING([for Qt5DBus >= 5.14])
PKG_CHECK_EXISTS([Qt5DBus >= 5.14],
[AC_MSG_CHECKING([for Qt5DBus >= 5.15.2])
PKG_CHECK_EXISTS([Qt5DBus >= 5.15.2],
[AC_MSG_RESULT([found])
HAVE_QTDBUS=[true]],
[AC_MSG_RESULT([not found])

View File

@@ -1,47 +1,66 @@
if (UNIX AND (NOT APPLE) AND (NOT CYGWIN))
find_package(LibtorrentRasterbar QUIET ${minLibtorrentVersion} COMPONENTS torrent-rasterbar)
if (NOT LibtorrentRasterbar_FOUND)
include(FindPkgConfig)
pkg_check_modules(LIBTORRENT_RASTERBAR IMPORTED_TARGET GLOBAL "libtorrent-rasterbar>=${minLibtorrentVersion}")
if (NOT LIBTORRENT_RASTERBAR_FOUND)
message(
FATAL_ERROR
"Package LibtorrentRasterbar >= ${minLibtorrentVersion} not found"
" with CMake or pkg-config.\n- Set LibtorrentRasterbar_DIR to a directory containing"
" a LibtorrentRasterbarConfig.cmake file or add the installation prefix of LibtorrentRasterbar"
" to CMAKE_PREFIX_PATH.\n- Alternatively, make sure there is a valid libtorrent-rasterbar.pc"
" file in your system's pkg-config search paths (use the system environment variable PKG_CONFIG_PATH"
" to specify additional search paths if needed)."
macro(find_libtorrent version)
if (UNIX AND (NOT APPLE) AND (NOT CYGWIN))
find_package(LibtorrentRasterbar QUIET ${version} COMPONENTS torrent-rasterbar)
if (NOT LibtorrentRasterbar_FOUND)
include(FindPkgConfig)
pkg_check_modules(LibtorrentRasterbar IMPORTED_TARGET GLOBAL "libtorrent-rasterbar>=${version}")
if (NOT LibtorrentRasterbar_FOUND)
message(
FATAL_ERROR
"Package LibtorrentRasterbar >= ${version} not found"
" with CMake or pkg-config.\n- Set LibtorrentRasterbar_DIR to a directory containing"
" a LibtorrentRasterbarConfig.cmake file or add the installation prefix of LibtorrentRasterbar"
" to CMAKE_PREFIX_PATH.\n- Alternatively, make sure there is a valid libtorrent-rasterbar.pc"
" file in your system's pkg-config search paths (use the system environment variable PKG_CONFIG_PATH"
" to specify additional search paths if needed)."
)
endif()
add_library(LibtorrentRasterbar::torrent-rasterbar ALIAS PkgConfig::LibtorrentRasterbar)
# force a fake package to show up in the feature summary
set_property(GLOBAL APPEND PROPERTY
PACKAGES_FOUND
"LibtorrentRasterbar via pkg-config (version >= ${version})"
)
set_package_properties("LibtorrentRasterbar via pkg-config (version >= ${version})"
PROPERTIES
TYPE REQUIRED
)
else()
set_package_properties(LibtorrentRasterbar PROPERTIES TYPE REQUIRED)
endif()
add_library(LibtorrentRasterbar::torrent-rasterbar ALIAS PkgConfig::LIBTORRENT_RASTERBAR)
# force a fake package to show up in the feature summary
set_property(GLOBAL APPEND PROPERTY
PACKAGES_FOUND
"LibtorrentRasterbar via pkg-config (version >= ${minLibtorrentVersion})"
)
set_package_properties("LibtorrentRasterbar via pkg-config (version >= ${minLibtorrentVersion})"
PROPERTIES
TYPE REQUIRED
)
else()
set_package_properties(LibtorrentRasterbar PROPERTIES TYPE REQUIRED)
find_package(LibtorrentRasterbar ${version} REQUIRED COMPONENTS torrent-rasterbar)
endif()
else()
find_package(LibtorrentRasterbar ${minLibtorrentVersion} REQUIRED COMPONENTS torrent-rasterbar)
endmacro()
find_libtorrent(${minLibtorrent1Version})
if (LibtorrentRasterbar_FOUND AND (LibtorrentRasterbar_VERSION VERSION_GREATER_EQUAL 2.0))
find_libtorrent(${minLibtorrentVersion})
endif()
# force variable type so that it always shows up in ccmake/cmake-gui frontends
set_property(CACHE LibtorrentRasterbar_DIR PROPERTY TYPE PATH)
find_package(Boost ${minBoostVersion} REQUIRED)
find_package(OpenSSL ${minOpenSSLVersion} REQUIRED)
find_package(ZLIB ${minZlibVersion} REQUIRED)
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS Core Network Sql Xml LinguistTools)
if (DBUS)
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS DBus)
set_package_properties(Qt5DBus PROPERTIES
DESCRIPTION "Qt5 module for inter-process communication over the D-Bus protocol"
PURPOSE "Required by the DBUS feature"
)
if (QT6)
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS Core Network Sql Xml LinguistTools)
if (DBUS)
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS DBus)
set_package_properties(Qt6DBus PROPERTIES
DESCRIPTION "Qt6 module for inter-process communication over the D-Bus protocol"
PURPOSE "Required by the DBUS feature"
)
endif()
else()
find_package(Qt5 ${minQt5Version} REQUIRED COMPONENTS Core Network Sql Xml LinguistTools)
if (DBUS)
find_package(Qt5 ${minQt5Version} REQUIRED COMPONENTS DBus)
set_package_properties(Qt5DBus PROPERTIES
DESCRIPTION "Qt5 module for inter-process communication over the D-Bus protocol"
PURPOSE "Required by the DBUS feature"
)
endif()
endif()
# automatically call Qt moc, rcc and uic as needed for all targets by default
@@ -60,10 +79,15 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_subdirectory(base)
if (GUI)
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS Widgets Svg)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS WinExtras)
if (QT6)
find_package(Qt6 ${minQt6Version} REQUIRED COMPONENTS Widgets Svg)
else()
find_package(Qt5 ${minQt5Version} REQUIRED COMPONENTS Widgets Svg)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
find_package(Qt5 ${minQt5Version} REQUIRED COMPONENTS WinExtras)
endif()
endif()
add_subdirectory(gui)
endif()

View File

@@ -4,14 +4,14 @@
# Based on https://gist.github.com/giraldeau/546ba5512a74dfe9d8ea0862d66db412
file(GLOB QBT_TS_FILES "${qBittorrent_SOURCE_DIR}/src/lang/*.ts")
set_source_files_properties(${QBT_TS_FILES} PROPERTIES OUTPUT_LOCATION "${qBittorrent_BINARY_DIR}/src/lang")
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES} OPTIONS -silent)
qt_add_translation(QBT_QM_FILES ${QBT_TS_FILES} OPTIONS -silent)
configure_file("${qBittorrent_SOURCE_DIR}/src/lang/lang.qrc" "${qBittorrent_BINARY_DIR}/src/lang/lang.qrc" COPYONLY)
if (WEBUI)
file(GLOB QBT_WEBUI_TS_FILES "${qBittorrent_SOURCE_DIR}/src/webui/www/translations/*.ts")
set_source_files_properties(${QBT_WEBUI_TS_FILES}
PROPERTIES OUTPUT_LOCATION "${qBittorrent_BINARY_DIR}/src/webui/www/translations")
qt5_add_translation(QBT_WEBUI_QM_FILES ${QBT_WEBUI_TS_FILES} OPTIONS -silent)
qt_add_translation(QBT_WEBUI_QM_FILES ${QBT_WEBUI_TS_FILES} OPTIONS -silent)
configure_file("${qBittorrent_SOURCE_DIR}/src/webui/www/translations/webui_translations.qrc"
"${qBittorrent_BINARY_DIR}/src/webui/www/translations/webui_translations.qrc" COPYONLY)
endif()
@@ -139,7 +139,7 @@ endif()
if (GUI)
target_link_libraries(qbt_app PRIVATE qbt_gui)
if ((CMAKE_SYSTEM_NAME STREQUAL "Windows") OR (CMAKE_SYSTEM_NAME STREQUAL "Darwin"))
qt_import_plugins(qbt_app INCLUDE Qt5::QSvgIconPlugin Qt5::QSvgPlugin)
qt_import_plugins(qbt_app INCLUDE Qt::QSvgIconPlugin Qt::QSvgPlugin)
endif()
endif()

View File

@@ -141,7 +141,9 @@ Application::Application(int &argc, char **argv)
setOrganizationDomain("qbittorrent.org");
#if !defined(DISABLE_GUI)
setDesktopFileName("org.qbittorrent.qBittorrent");
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
setAttribute(Qt::AA_UseHighDpiPixmaps, true); // opt-in to the high DPI pixmap support
#endif
setQuitOnLastWindowClosed(false);
QPixmapCache::setCacheLimit(PIXMAP_CACHE_SIZE);
#endif
@@ -435,16 +437,12 @@ void Application::runExternalProgram(const BitTorrent::Torrent *torrent) const
// enable command injection via torrent name and other arguments
// (especially when some automated download mechanism has been setup).
// See: https://github.com/qbittorrent/qBittorrent/issues/10925
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
QStringList args = QProcess::splitCommand(program);
if (args.isEmpty())
return;
const QString command = args.takeFirst();
QProcess::startDetached(command, args);
#else
QProcess::startDetached(program);
#endif
#endif
}
@@ -565,7 +563,7 @@ void Application::processParams(const QStringList &params)
if (param.startsWith(QLatin1String("@addPaused=")))
{
torrentParams.addPaused = (param.midRef(11).toInt() != 0);
torrentParams.addPaused = (QStringView(param).mid(11).toInt() != 0);
continue;
}
@@ -595,7 +593,7 @@ void Application::processParams(const QStringList &params)
if (param.startsWith(QLatin1String("@skipDialog=")))
{
skipTorrentDialog = (param.midRef(12).toInt() != 0);
skipTorrentDialog = (QStringView(param).mid(12).toInt() != 0);
continue;
}
@@ -669,8 +667,8 @@ int Application::exec(const QStringList &params)
if (pref->getWebUIPassword() == "ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ==")
{
const QString warning = tr("The Web UI administrator username is: %1").arg(pref->getWebUiUsername()) + '\n'
+ tr("The Web UI administrator password is still the default one: %1").arg("adminadmin") + '\n'
+ tr("This is a security risk, please consider changing your password from program preferences.") + '\n';
+ tr("The Web UI administrator password has not been changed from the default: %1").arg("adminadmin") + '\n'
+ tr("This is a security risk, please change your password in program preferences.") + '\n';
printf("%s", qUtf8Printable(warning));
}
#endif // DISABLE_WEBUI

View File

@@ -71,7 +71,7 @@ namespace RSS
class Application final : public BaseApplication
{
Q_OBJECT
Q_DISABLE_COPY(Application)
Q_DISABLE_COPY_MOVE(Application)
public:
Application(int &argc, char **argv);

View File

@@ -35,7 +35,7 @@ class QtLocalPeer;
class ApplicationInstanceManager : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(ApplicationInstanceManager)
Q_DISABLE_COPY_MOVE(ApplicationInstanceManager)
public:
explicit ApplicationInstanceManager(const QString &appId, QObject *parent = nullptr);

View File

@@ -526,55 +526,55 @@ QString makeUsage(const QString &prgName)
QTextStream stream(&text, QIODevice::WriteOnly);
QString indentation = QString(USAGE_INDENTATION, ' ');
stream << QObject::tr("Usage:") << '\n';
stream << indentation << prgName << QLatin1String(" [options] [(<filename> | <url>)...]") << '\n';
stream << QObject::tr("Usage:") << '\n'
<< indentation << prgName << QLatin1String(" [options] [(<filename> | <url>)...]") << '\n'
stream << QObject::tr("Options:") << '\n';
<< QObject::tr("Options:") << '\n'
#if !defined(Q_OS_WIN) || defined(DISABLE_GUI)
stream << SHOW_VERSION_OPTION.usage() << wrapText(QObject::tr("Display program version and exit")) << '\n';
<< SHOW_VERSION_OPTION.usage() << wrapText(QObject::tr("Display program version and exit")) << '\n'
#endif
stream << SHOW_HELP_OPTION.usage() << wrapText(QObject::tr("Display this help message and exit")) << '\n';
stream << WEBUI_PORT_OPTION.usage(QObject::tr("port"))
<< wrapText(QObject::tr("Change the Web UI port"))
<< '\n';
<< SHOW_HELP_OPTION.usage() << wrapText(QObject::tr("Display this help message and exit")) << '\n'
<< WEBUI_PORT_OPTION.usage(QObject::tr("port"))
<< wrapText(QObject::tr("Change the Web UI port"))
<< '\n'
#ifndef DISABLE_GUI
stream << NO_SPLASH_OPTION.usage() << wrapText(QObject::tr("Disable splash screen")) << '\n';
<< NO_SPLASH_OPTION.usage() << wrapText(QObject::tr("Disable splash screen")) << '\n'
#elif !defined(Q_OS_WIN)
stream << DAEMON_OPTION.usage() << wrapText(QObject::tr("Run in daemon-mode (background)")) << '\n';
<< DAEMON_OPTION.usage() << wrapText(QObject::tr("Run in daemon-mode (background)")) << '\n'
#endif
//: Use appropriate short form or abbreviation of "directory"
stream << PROFILE_OPTION.usage(QObject::tr("dir"))
<< wrapText(QObject::tr("Store configuration files in <dir>")) << '\n';
stream << CONFIGURATION_OPTION.usage(QObject::tr("name"))
<< wrapText(QObject::tr("Store configuration files in directories qBittorrent_<name>")) << '\n';
stream << RELATIVE_FASTRESUME.usage()
<< wrapText(QObject::tr("Hack into libtorrent fastresume files and make file paths relative "
"to the profile directory")) << '\n';
stream << Option::padUsageText(QObject::tr("files or URLs"))
<< wrapText(QObject::tr("Download the torrents passed by the user")) << '\n'
<< '\n';
<< PROFILE_OPTION.usage(QObject::tr("dir"))
<< wrapText(QObject::tr("Store configuration files in <dir>")) << '\n'
<< CONFIGURATION_OPTION.usage(QObject::tr("name"))
<< wrapText(QObject::tr("Store configuration files in directories qBittorrent_<name>")) << '\n'
<< RELATIVE_FASTRESUME.usage()
<< wrapText(QObject::tr("Hack into libtorrent fastresume files and make file paths relative "
"to the profile directory")) << '\n'
<< Option::padUsageText(QObject::tr("files or URLs"))
<< wrapText(QObject::tr("Download the torrents passed by the user")) << '\n'
<< '\n'
stream << wrapText(QObject::tr("Options when adding new torrents:"), 0) << '\n';
stream << SAVE_PATH_OPTION.usage(QObject::tr("path")) << wrapText(QObject::tr("Torrent save path")) << '\n';
stream << PAUSED_OPTION.usage() << wrapText(QObject::tr("Add torrents as started or paused")) << '\n';
stream << SKIP_HASH_CHECK_OPTION.usage() << wrapText(QObject::tr("Skip hash check")) << '\n';
stream << CATEGORY_OPTION.usage(QObject::tr("name"))
<< wrapText(QObject::tr("Assign torrents to category. If the category doesn't exist, it will be "
"created.")) << '\n';
stream << SEQUENTIAL_OPTION.usage() << wrapText(QObject::tr("Download files in sequential order")) << '\n';
stream << FIRST_AND_LAST_OPTION.usage()
<< wrapText(QObject::tr("Download first and last pieces first")) << '\n';
stream << SKIP_DIALOG_OPTION.usage()
<< wrapText(QObject::tr("Specify whether the \"Add New Torrent\" dialog opens when adding a "
"torrent.")) << '\n';
stream << '\n';
<< wrapText(QObject::tr("Options when adding new torrents:"), 0) << '\n'
<< SAVE_PATH_OPTION.usage(QObject::tr("path")) << wrapText(QObject::tr("Torrent save path")) << '\n'
<< PAUSED_OPTION.usage() << wrapText(QObject::tr("Add torrents as started or paused")) << '\n'
<< SKIP_HASH_CHECK_OPTION.usage() << wrapText(QObject::tr("Skip hash check")) << '\n'
<< CATEGORY_OPTION.usage(QObject::tr("name"))
<< wrapText(QObject::tr("Assign torrents to category. If the category doesn't exist, it will be "
"created.")) << '\n'
<< SEQUENTIAL_OPTION.usage() << wrapText(QObject::tr("Download files in sequential order")) << '\n'
<< FIRST_AND_LAST_OPTION.usage()
<< wrapText(QObject::tr("Download first and last pieces first")) << '\n'
<< SKIP_DIALOG_OPTION.usage()
<< wrapText(QObject::tr("Specify whether the \"Add New Torrent\" dialog opens when adding a "
"torrent.")) << '\n'
<< '\n'
stream << wrapText(QObject::tr("Option values may be supplied via environment variables. For option named "
"'parameter-name', environment variable name is 'QBT_PARAMETER_NAME' (in upper "
"case, '-' replaced with '_'). To pass flag values, set the variable to '1' or "
"'TRUE'. For example, to disable the splash screen: "), 0) << "\n"
<< QLatin1String("QBT_NO_SPLASH=1 ") << prgName << '\n'
<< wrapText(QObject::tr("Command line parameters take precedence over environment variables"), 0) << '\n';
<< wrapText(QObject::tr("Option values may be supplied via environment variables. For option named "
"'parameter-name', environment variable name is 'QBT_PARAMETER_NAME' (in upper "
"case, '-' replaced with '_'). To pass flag values, set the variable to '1' or "
"'TRUE'. For example, to disable the splash screen: "), 0) << "\n"
<< QLatin1String("QBT_NO_SPLASH=1 ") << prgName << '\n'
<< wrapText(QObject::tr("Command line parameters take precedence over environment variables"), 0) << '\n';
return text;
}

View File

@@ -179,7 +179,7 @@ void FileLogger::openLogFile()
{
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)
|| !m_logFile.setPermissions(QFile::ReadOwner | QFile::WriteOwner))
{
{
m_logFile.close();
LogMsg(tr("An error occurred while trying to open the log file. Logging to file is disabled."), Log::CRITICAL);
}

View File

@@ -40,7 +40,7 @@ namespace Log
class FileLogger : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(FileLogger)
Q_DISABLE_COPY_MOVE(FileLogger)
public:
enum FileLogAgeType

View File

@@ -134,7 +134,7 @@ int main(int argc, char *argv[])
// We must save it here because QApplication constructor may change it
bool isOneArg = (argc == 2);
#if !defined(DISABLE_GUI)
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && !defined(DISABLE_GUI)
// Attribute Qt::AA_EnableHighDpiScaling must be set before QCoreApplication is created
if (qgetenv("QT_ENABLE_HIGHDPI_SCALING").isEmpty() && qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR").isEmpty())
Application::setAttribute(Qt::AA_EnableHighDpiScaling, true);

View File

@@ -89,7 +89,7 @@ Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate)
Qt::HANDLE mutex;
if (doCreate) {
QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); },
QT_WA( { mutex = CreateMutexW(NULL, FALSE, reinterpret_cast<const TCHAR *>(mname.utf16())); },
{ mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } );
if (!mutex) {
qErrnoWarning("QtLockedFile::lock(): CreateMutex failed");
@@ -97,7 +97,7 @@ Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate)
}
}
else {
QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); },
QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, reinterpret_cast<const TCHAR *>(mname.utf16())); },
{ mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } );
if (!mutex) {
if (GetLastError() != ERROR_FILE_NOT_FOUND)

View File

@@ -73,7 +73,7 @@ void straceWin::loadHelpStackFrame(IMAGEHLP_STACK_FRAME& ihsf, const STACKFRAME6
BOOL CALLBACK straceWin::EnumSymbolsCB(PSYMBOL_INFO symInfo, ULONG size, PVOID user)
{
Q_UNUSED(size)
QStringList* params = (QStringList*)user;
auto params = static_cast<QStringList *>(user);
if (symInfo->Flags & SYMFLAG_PARAMETER)
params->append(symInfo->Name);
return TRUE;
@@ -91,7 +91,7 @@ BOOL CALLBACK straceWin::EnumModulesCB(LPCSTR ModuleName, DWORD64 BaseOfDll, PVO
{
Q_UNUSED(ModuleName)
IMAGEHLP_MODULE64 mod;
EnumModulesContext* context = (EnumModulesContext*)UserContext;
auto context = static_cast<EnumModulesContext *>(UserContext);
mod.SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
if(SymGetModuleInfo64(context->hProcess, BaseOfDll, &mod))
{
@@ -264,7 +264,7 @@ const QString straceWin::getBacktrace()
ULONG64 buffer[(sizeof(SYMBOL_INFO) +
MAX_SYM_NAME * sizeof(TCHAR) +
sizeof(ULONG64) - 1) / sizeof(ULONG64)];
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
auto pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;

View File

@@ -39,7 +39,7 @@ namespace Ui
class StacktraceDialog : public QDialog
{
Q_OBJECT
Q_DISABLE_COPY(StacktraceDialog)
Q_DISABLE_COPY_MOVE(StacktraceDialog)
public:
explicit StacktraceDialog(QWidget *parent = nullptr);

View File

@@ -28,7 +28,6 @@
#include "upgrade.h"
#include <QFile>
#include <QMetaEnum>
#include <QVector>
@@ -37,6 +36,7 @@
#include "base/profile.h"
#include "base/settingsstorage.h"
#include "base/utils/fs.h"
#include "base/utils/io.h"
#include "base/utils/string.h"
namespace
@@ -53,17 +53,10 @@ namespace
if (!newData.isEmpty() || oldData.isEmpty())
return;
QFile file(savePath);
if (!file.open(QIODevice::WriteOnly))
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(savePath, oldData);
if (!result)
{
LogMsg(errorMsgFormat.arg(savePath, file.errorString()) , Log::WARNING);
return;
}
if (file.write(oldData) != oldData.size())
{
file.close();
Utils::Fs::forceRemove(savePath);
LogMsg(errorMsgFormat.arg(savePath, QLatin1String("Write incomplete.")) , Log::WARNING);
LogMsg(errorMsgFormat.arg(savePath, result.error()) , Log::WARNING);
return;
}
@@ -102,12 +95,28 @@ namespace
settingsStorage->storeValue(newKey, Utils::String::fromEnum(torrentContentLayout));
settingsStorage->removeValue(oldKey);
}
void upgradeListenPortSettings()
{
const auto oldKey = QString::fromLatin1("BitTorrent/Session/UseRandomPort");
const auto newKey = QString::fromLatin1("Preferences/Connection/PortRangeMin");
auto *settingsStorage = SettingsStorage::instance();
if (settingsStorage->hasKey(oldKey))
{
if (settingsStorage->loadValue<bool>(oldKey))
settingsStorage->storeValue(newKey, 0);
settingsStorage->removeValue(oldKey);
}
}
}
bool upgrade(const bool /*ask*/)
{
exportWebUIHttpsFiles();
upgradeTorrentContentLayout();
upgradeListenPortSettings();
return true;
}

2517
src/base/3rdparty/expected.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
add_library(qbt_base STATIC
# headers
3rdparty/expected.hpp
algorithm.h
asyncfilestorage.h
bittorrent/abstractfilestorage.h
@@ -16,7 +17,7 @@ add_library(qbt_base STATIC
bittorrent/infohash.h
bittorrent/loadtorrentparams.h
bittorrent/ltqhash.h
bittorrent/ltunderlyingtype.h
bittorrent/lttypecast.h
bittorrent/magneturi.h
bittorrent/nativesessionextension.h
bittorrent/nativetorrentextension.h
@@ -176,7 +177,7 @@ target_link_libraries(qbt_base
ZLIB::ZLIB
PUBLIC
LibtorrentRasterbar::torrent-rasterbar
Qt5::Core Qt5::Network Qt5::Sql Qt5::Xml
Qt::Core Qt::Network Qt::Sql Qt::Xml
qbt_common_cfg
)
@@ -203,5 +204,5 @@ if (NOT WEBUI)
endif()
if (DBUS)
target_link_libraries(qbt_base PUBLIC Qt5::DBus)
target_link_libraries(qbt_base PUBLIC Qt::DBus)
endif()

View File

@@ -30,7 +30,8 @@
#include <QDebug>
#include <QMetaObject>
#include <QSaveFile>
#include "base/utils/io.h"
AsyncFileStorage::AsyncFileStorage(const QString &storageFolderPath, QObject *parent)
: QObject(parent)
@@ -67,15 +68,12 @@ QDir AsyncFileStorage::storageDir() const
void AsyncFileStorage::store_impl(const QString &fileName, const QByteArray &data)
{
const QString filePath = m_storageDir.absoluteFilePath(fileName);
QSaveFile file(filePath);
qDebug() << "AsyncFileStorage: Saving data to" << filePath;
if (file.open(QIODevice::WriteOnly))
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(filePath, data);
if (!result)
{
file.write(data);
if (!file.commit())
{
qDebug() << "AsyncFileStorage: Failed to save data";
emit failed(filePath, file.errorString());
}
qDebug() << "AsyncFileStorage: Failed to save data";
emit failed(filePath, result.error());
}
}

View File

@@ -43,7 +43,7 @@ public:
class AsyncFileStorage : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(AsyncFileStorage)
Q_DISABLE_COPY_MOVE(AsyncFileStorage)
public:
explicit AsyncFileStorage(const QString &storageFolderPath, QObject *parent = nullptr);

View File

@@ -1,4 +1,5 @@
HEADERS += \
$$PWD/3rdparty/expected.hpp \
$$PWD/algorithm.h \
$$PWD/asyncfilestorage.h \
$$PWD/bittorrent/abstractfilestorage.h \
@@ -15,7 +16,7 @@ HEADERS += \
$$PWD/bittorrent/infohash.h \
$$PWD/bittorrent/loadtorrentparams.h \
$$PWD/bittorrent/ltqhash.h \
$$PWD/bittorrent/ltunderlyingtype.h \
$$PWD/bittorrent/lttypecast.h \
$$PWD/bittorrent/magneturi.h \
$$PWD/bittorrent/nativesessionextension.h \
$$PWD/bittorrent/nativetorrentextension.h \

View File

@@ -44,7 +44,6 @@ namespace BitTorrent
virtual int filesCount() const = 0;
virtual QString filePath(int index) const = 0;
virtual QString fileName(int index) const = 0;
virtual qlonglong fileSize(int index) const = 0;
virtual void renameFile(int index, const QString &name) = 0;

View File

@@ -35,7 +35,7 @@
class BandwidthScheduler : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(BandwidthScheduler)
Q_DISABLE_COPY_MOVE(BandwidthScheduler)
public:
explicit BandwidthScheduler(QObject *parent = nullptr);

View File

@@ -29,15 +29,14 @@
#include "bencoderesumedatastorage.h"
#include <libtorrent/bdecode.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/read_resume_data.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/write_resume_data.hpp>
#include <QByteArray>
#include <QDebug>
#include <QRegularExpression>
#include <QSaveFile>
#include <QThread>
#include "base/algorithm.h"
@@ -51,13 +50,12 @@
#include "base/utils/string.h"
#include "infohash.h"
#include "loadtorrentparams.h"
#include "torrentinfo.h"
namespace BitTorrent
{
class BencodeResumeDataStorage::Worker final : public QObject
{
Q_DISABLE_COPY(Worker)
Q_DISABLE_COPY_MOVE(Worker)
public:
explicit Worker(const QDir &resumeDataDir);
@@ -89,17 +87,6 @@ namespace
entryList.emplace_back(setValue.toStdString());
return entryList;
}
void writeEntryToFile(const QString &filepath, const lt::entry &data)
{
QSaveFile file {filepath};
if (!file.open(QIODevice::WriteOnly))
throw RuntimeError(file.errorString());
lt::bencode(Utils::IO::FileDeviceOutputIterator {file}, data);
if (file.error() != QFileDevice::NoError || !file.commit())
throw RuntimeError(file.errorString());
}
}
BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const QString &path, QObject *parent)
@@ -127,7 +114,7 @@ BitTorrent::BencodeResumeDataStorage::BencodeResumeDataStorage(const QString &pa
loadQueue(m_resumeDataDir.absoluteFilePath(QLatin1String("queue")));
qDebug("Registered torrents count: %d", m_registeredTorrents.size());
qDebug() << "Registered torrents count: " << m_registeredTorrents.size();
m_asyncWorker->moveToThread(m_ioThread);
connect(m_ioThread, &QThread::finished, m_asyncWorker, &QObject::deleteLater);
@@ -151,25 +138,36 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorag
const QString fastresumePath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(idString));
const QString torrentFilePath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(idString));
QFile file {fastresumePath};
if (!file.open(QIODevice::ReadOnly))
QFile resumeDataFile {fastresumePath};
if (!resumeDataFile.open(QIODevice::ReadOnly))
{
LogMsg(tr("Cannot read file %1: %2").arg(fastresumePath, file.errorString()), Log::WARNING);
LogMsg(tr("Cannot read file %1: %2").arg(fastresumePath, resumeDataFile.errorString()), Log::WARNING);
return std::nullopt;
}
const QByteArray data = file.readAll();
const TorrentInfo metadata = TorrentInfo::loadFromFile(torrentFilePath);
QFile metadataFile {torrentFilePath};
if (metadataFile.exists() && !metadataFile.open(QIODevice::ReadOnly))
{
LogMsg(tr("Cannot read file %1: %2").arg(torrentFilePath, metadataFile.errorString()), Log::WARNING);
return std::nullopt;
}
const QByteArray data = resumeDataFile.readAll();
const QByteArray metadata = (metadataFile.isOpen() ? metadataFile.readAll() : "");
return loadTorrentResumeData(data, metadata);
}
std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorage::loadTorrentResumeData(
const QByteArray &data, const TorrentInfo &metadata) const
const QByteArray &data, const QByteArray &metadata) const
{
const QByteArray allData = ((metadata.isEmpty() || data.isEmpty())
? data : (data.chopped(1) + metadata.mid(1)));
lt::error_code ec;
const lt::bdecode_node root = lt::bdecode(data, ec);
if (ec || (root.type() != lt::bdecode_node::dict_t)) return std::nullopt;
const lt::bdecode_node root = lt::bdecode(allData, ec);
if (ec || (root.type() != lt::bdecode_node::dict_t))
return std::nullopt;
LoadTorrentParams torrentParams;
torrentParams.restored = true;
@@ -220,8 +218,6 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorag
p = lt::read_resume_data(root, ec);
p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
if (metadata.isValid())
p.ti = metadata.nativeInfo();
if (p.flags & lt::torrent_flags::stop_when_ready)
{
@@ -333,33 +329,29 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
}
}
lt::entry data = lt::write_resume_data(p);
// metadata is stored in separate .torrent file
const std::shared_ptr<lt::torrent_info> torrentInfo = std::move(p.ti);
if (torrentInfo)
if (p.ti)
{
lt::entry::dictionary_type &dataDict = data.dict();
lt::entry metadata {lt::entry::dictionary_t};
lt::entry::dictionary_type &metadataDict = metadata.dict();
metadataDict.insert(dataDict.extract("info"));
metadataDict.insert(dataDict.extract("creation date"));
metadataDict.insert(dataDict.extract("created by"));
metadataDict.insert(dataDict.extract("comment"));
const QString torrentFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.torrent").arg(id.toString()));
try
{
const auto torrentCreator = lt::create_torrent(*torrentInfo);
const lt::entry metadata = torrentCreator.generate();
writeEntryToFile(torrentFilepath, metadata);
}
catch (const RuntimeError &err)
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(torrentFilepath, metadata);
if (!result)
{
LogMsg(tr("Couldn't save torrent metadata to '%1'. Error: %2.")
.arg(torrentFilepath, err.message()), Log::CRITICAL);
return;
}
catch (const std::exception &err)
{
LogMsg(tr("Couldn't save torrent metadata to '%1'. Error: %2.")
.arg(torrentFilepath, QString::fromLocal8Bit(err.what())), Log::CRITICAL);
.arg(torrentFilepath, result.error()), Log::CRITICAL);
return;
}
}
lt::entry data = lt::write_resume_data(p);
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).toStdString();
data["qBt-ratioLimit"] = static_cast<int>(resumeData.ratioLimit * 1000);
data["qBt-seedingTimeLimit"] = resumeData.seedingTimeLimit;
@@ -371,14 +363,11 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
data["qBt-firstLastPiecePriority"] = resumeData.firstLastPiecePriority;
const QString resumeFilepath = m_resumeDataDir.absoluteFilePath(QString::fromLatin1("%1.fastresume").arg(id.toString()));
try
{
writeEntryToFile(resumeFilepath, data);
}
catch (const RuntimeError &err)
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(resumeFilepath, data);
if (!result)
{
LogMsg(tr("Couldn't save torrent resume data to '%1'. Error: %2.")
.arg(resumeFilepath, err.message()), Log::CRITICAL);
.arg(resumeFilepath, result.error()), Log::CRITICAL);
}
}
@@ -399,10 +388,10 @@ void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<Torr
data += (torrentID.toString().toLatin1() + '\n');
const QString filepath = m_resumeDataDir.absoluteFilePath(QLatin1String("queue"));
QSaveFile file {filepath};
if (!file.open(QIODevice::WriteOnly) || (file.write(data) != data.size()) || !file.commit())
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(filepath, data);
if (!result)
{
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::CRITICAL);
.arg(filepath, result.error()), Log::CRITICAL);
}
}

View File

@@ -38,12 +38,10 @@ class QThread;
namespace BitTorrent
{
class TorrentInfo;
class BencodeResumeDataStorage final : public ResumeDataStorage
{
Q_OBJECT
Q_DISABLE_COPY(BencodeResumeDataStorage)
Q_DISABLE_COPY_MOVE(BencodeResumeDataStorage)
public:
explicit BencodeResumeDataStorage(const QString &path, QObject *parent = nullptr);
@@ -57,7 +55,7 @@ namespace BitTorrent
private:
void loadQueue(const QString &queueFilename);
std::optional<LoadTorrentParams> loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata) const;
std::optional<LoadTorrentParams> loadTorrentResumeData(const QByteArray &data, const QByteArray &metadata) const;
const QDir m_resumeDataDir;
QVector<TorrentID> m_registeredTorrents;

View File

@@ -35,7 +35,7 @@
#include "base/utils/fs.h"
#include "common.h"
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
#include <libtorrent/session.hpp>
std::unique_ptr<lt::disk_interface> customDiskIOConstructor(

View File

@@ -30,11 +30,10 @@
#include <libtorrent/aux_/vector.hpp>
#include <libtorrent/fwd.hpp>
#include <libtorrent/version.hpp>
#include <QString>
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
#include <libtorrent/disk_interface.hpp>
#include <libtorrent/file_storage.hpp>
#include <libtorrent/io_context.hpp>
@@ -46,7 +45,7 @@
#include <libtorrent/storage.hpp>
#endif
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
std::unique_ptr<lt::disk_interface> customDiskIOConstructor(
lt::io_context &ioContext, lt::settings_interface const &settings, lt::counters &counters);

View File

@@ -30,9 +30,9 @@
#include <libtorrent/bdecode.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/read_resume_data.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/write_resume_data.hpp>
#include <QByteArray>
@@ -52,7 +52,6 @@
#include "base/utils/string.h"
#include "infohash.h"
#include "loadtorrentparams.h"
#include "torrentinfo.h"
namespace
{
@@ -158,7 +157,7 @@ namespace BitTorrent
{
class DBResumeDataStorage::Worker final : public QObject
{
Q_DISABLE_COPY(Worker)
Q_DISABLE_COPY_MOVE(Worker)
public:
Worker(const QString &dbPath, const QString &dbConnectionName);
@@ -290,20 +289,19 @@ std::optional<BitTorrent::LoadTorrentParams> BitTorrent::DBResumeDataStorage::lo
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray();
const QByteArray allData = ((bencodedMetadata.isEmpty() || bencodedResumeData.isEmpty())
? bencodedResumeData
: (bencodedResumeData.chopped(1) + bencodedMetadata.mid(1)));
lt::error_code ec;
const lt::bdecode_node root = lt::bdecode(bencodedResumeData, ec);
const lt::bdecode_node root = lt::bdecode(allData, ec);
lt::add_torrent_params &p = resumeData.ltAddTorrentParams;
p = lt::read_resume_data(root, ec);
p.save_path = Profile::instance()->fromPortablePath(fromLTString(p.save_path)).toStdString();
const QByteArray bencodedMetadata = query.value(DB_COLUMN_METADATA.name).toByteArray();
auto metadata = TorrentInfo::load(bencodedMetadata);
if (metadata.isValid())
p.ti = metadata.nativeInfo();
return resumeData;
}
@@ -453,16 +451,23 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
DB_COLUMN_RESUMEDATA
};
lt::entry data = lt::write_resume_data(p);
// metadata is stored in separate column
QByteArray bencodedMetadata;
bencodedMetadata.reserve(512 * 1024);
const std::shared_ptr<lt::torrent_info> torrentInfo = std::move(p.ti);
if (torrentInfo)
if (p.ti)
{
lt::entry::dictionary_type &dataDict = data.dict();
lt::entry metadata {lt::entry::dictionary_t};
lt::entry::dictionary_type &metadataDict = metadata.dict();
metadataDict.insert(dataDict.extract("info"));
metadataDict.insert(dataDict.extract("creation date"));
metadataDict.insert(dataDict.extract("created by"));
metadataDict.insert(dataDict.extract("comment"));
try
{
const auto torrentCreator = lt::create_torrent(*torrentInfo);
const lt::entry metadata = torrentCreator.generate();
bencodedMetadata.reserve(512 * 1024);
lt::bencode(std::back_inserter(bencodedMetadata), metadata);
}
catch (const std::exception &err)
@@ -477,7 +482,7 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
QByteArray bencodedResumeData;
bencodedResumeData.reserve(256 * 1024);
lt::bencode(std::back_inserter(bencodedResumeData), lt::write_resume_data(p));
lt::bencode(std::back_inserter(bencodedResumeData), data);
const QString insertTorrentStatement = makeInsertStatement(DB_TABLE_TORRENTS, columns)
+ makeOnConflictUpdateStatement(DB_COLUMN_TORRENT_ID, columns);
@@ -503,7 +508,7 @@ void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const L
query.bindValue(DB_COLUMN_OPERATING_MODE.placeholder, Utils::String::fromEnum(resumeData.operatingMode));
query.bindValue(DB_COLUMN_STOPPED.placeholder, resumeData.stopped);
query.bindValue(DB_COLUMN_RESUMEDATA.placeholder, bencodedResumeData);
if (torrentInfo)
if (!bencodedMetadata.isEmpty())
query.bindValue(DB_COLUMN_METADATA.placeholder, bencodedMetadata);
if (!query.exec())

View File

@@ -37,7 +37,7 @@ namespace BitTorrent
class DBResumeDataStorage final : public ResumeDataStorage
{
Q_OBJECT
Q_DISABLE_COPY(DBResumeDataStorage)
Q_DISABLE_COPY_MOVE(DBResumeDataStorage)
public:
explicit DBResumeDataStorage(const QString &dbPath, QObject *parent = nullptr);

View File

@@ -38,7 +38,7 @@ namespace BitTorrent
class FileSearcher final : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(FileSearcher)
Q_DISABLE_COPY_MOVE(FileSearcher)
public:
FileSearcher() = default;

View File

@@ -43,7 +43,7 @@ bool BitTorrent::InfoHash::isValid() const
SHA1Hash BitTorrent::InfoHash::v1() const
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
return (m_nativeHash.has_v1() ? SHA1Hash(m_nativeHash.v1) : SHA1Hash());
#else
return {m_nativeHash};
@@ -52,7 +52,7 @@ SHA1Hash BitTorrent::InfoHash::v1() const
SHA256Hash BitTorrent::InfoHash::v2() const
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
return (m_nativeHash.has_v2() ? SHA256Hash(m_nativeHash.v2) : SHA256Hash());
#else
return {};
@@ -61,7 +61,7 @@ SHA256Hash BitTorrent::InfoHash::v2() const
BitTorrent::TorrentID BitTorrent::InfoHash::toTorrentID() const
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
return m_nativeHash.get_best();
#else
return {m_nativeHash};

View File

@@ -28,8 +28,7 @@
#pragma once
#include <libtorrent/version.hpp>
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
#include <libtorrent/info_hash.hpp>
#endif
@@ -58,7 +57,7 @@ namespace BitTorrent
class InfoHash
{
public:
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
using WrappedType = lt::info_hash_t;
#else
using WrappedType = lt::sha1_hash;

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2019 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2019, 2021 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -28,31 +28,25 @@
#pragma once
#include <type_traits>
#include <libtorrent/download_priority.hpp>
template <typename T, typename = void>
struct HasUnderlyingType
: std::false_type
#include "downloadpriority.h"
namespace BitTorrent::LT
{
};
template <typename T>
constexpr typename T::underlying_type toUnderlyingType(const T &t) noexcept
{
return static_cast<typename T::underlying_type>(t);
}
template <typename T>
struct HasUnderlyingType<T, std::void_t<typename T::underlying_type>>
: std::true_type
{
};
constexpr lt::download_priority_t toNative(const DownloadPriority priority) noexcept
{
return static_cast<lt::download_priority_t>(static_cast<lt::download_priority_t::underlying_type>(priority));
}
template <typename T, typename = void>
struct LTUnderlying
{
using type = T;
};
template <typename T>
struct LTUnderlying<T, typename std::enable_if_t<HasUnderlyingType<T>::value>>
{
using type = typename T::underlying_type;
};
template <typename T>
using LTUnderlyingType = typename LTUnderlying<T>::type;
constexpr DownloadPriority fromNative(const lt::download_priority_t priority) noexcept
{
return static_cast<DownloadPriority>(toUnderlyingType(priority));
}
}

View File

@@ -39,21 +39,34 @@
namespace
{
bool isSHA1Hash(const QString &string)
// BEP9 Extension for Peers to Send Metadata Files
bool isV1Hash(const QString &string)
{
// There are 2 representations for BitTorrent info hash:
// There are 2 representations for BitTorrent v1 info hash:
// 1. 40 chars hex-encoded string
// == 20 (SHA-1 length in bytes) * 2 (each byte maps to 2 hex characters)
// 2. 32 chars Base32 encoded string
// == 20 (SHA-1 length in bytes) * 1.6 (the efficiency of Base32 encoding)
const int SHA1_HEX_SIZE = SHA1Hash::length() * 2;
const int SHA1_BASE32_SIZE = SHA1Hash::length() * 1.6;
const int V1_HEX_SIZE = SHA1Hash::length() * 2;
const int V1_BASE32_SIZE = SHA1Hash::length() * 1.6;
return ((((string.size() == SHA1_HEX_SIZE))
return ((((string.size() == V1_HEX_SIZE))
&& !string.contains(QRegularExpression(QLatin1String("[^0-9A-Fa-f]"))))
|| ((string.size() == SHA1_BASE32_SIZE)
|| ((string.size() == V1_BASE32_SIZE)
&& !string.contains(QRegularExpression(QLatin1String("[^2-7A-Za-z]")))));
}
bool isV2Hash(const QString &string)
{
// There are 1 representation for BitTorrent v2 info hash:
// 1. 64 chars hex-encoded string
// == 32 (SHA-2 256 length in bytes) * 2 (each byte maps to 2 hex characters)
const int V2_HEX_SIZE = SHA256Hash::length() * 2;
return (string.size() == V2_HEX_SIZE)
&& !string.contains(QRegularExpression(QLatin1String("[^0-9A-Fa-f]")));
}
}
using namespace BitTorrent;
@@ -66,8 +79,10 @@ MagnetUri::MagnetUri(const QString &source)
{
if (source.isEmpty()) return;
if (isSHA1Hash(source))
m_url = QLatin1String("magnet:?xt=urn:btih:") + source;
if (isV2Hash(source))
m_url = QString::fromLatin1("magnet:?xt=urn:btmh:1220") + source; // 0x12 0x20 is the "multihash format" tag for the SHA-256 hashing scheme.
else if (isV1Hash(source))
m_url = QString::fromLatin1("magnet:?xt=urn:btih:") + source;
lt::error_code ec;
lt::parse_magnet_uri(m_url.toStdString(), m_addTorrentParams, ec);
@@ -75,7 +90,7 @@ MagnetUri::MagnetUri(const QString &source)
m_valid = true;
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
m_infoHash = m_addTorrentParams.info_hashes;
#else
m_infoHash = m_addTorrentParams.info_hash;

View File

@@ -29,11 +29,10 @@
#pragma once
#include <libtorrent/extensions.hpp>
#include <libtorrent/version.hpp>
class NativeSessionExtension final : public lt::plugin
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
using ClientData = lt::client_data_t;
#else
using ClientData = void *;

View File

@@ -32,18 +32,18 @@
using namespace BitTorrent;
PeerAddress PeerAddress::parse(const QString &address)
PeerAddress PeerAddress::parse(const QStringView address)
{
QVector<QStringRef> ipPort;
QList<QStringView> ipPort;
if (address.startsWith('[') && address.contains("]:"))
if (address.startsWith(u'[') && address.contains(QLatin1String("]:")))
{ // IPv6
ipPort = address.splitRef("]:");
ipPort = address.split(QString::fromLatin1("]:"));
ipPort[0] = ipPort[0].mid(1); // chop '['
}
else if (address.contains(':'))
else if (address.contains(u':'))
{ // IPv4
ipPort = address.splitRef(':');
ipPort = address.split(u':');
}
else
{

View File

@@ -39,7 +39,7 @@ namespace BitTorrent
QHostAddress ip;
ushort port = 0;
static PeerAddress parse(const QString &address);
static PeerAddress parse(QStringView address);
QString toString() const;
};

View File

@@ -40,7 +40,7 @@
class PortForwarderImpl final : public Net::PortForwarder
{
Q_OBJECT
Q_DISABLE_COPY(PortForwarderImpl)
Q_DISABLE_COPY_MOVE(PortForwarderImpl)
public:
explicit PortForwarderImpl(lt::session *provider, QObject *parent = nullptr);

View File

@@ -41,7 +41,7 @@ namespace BitTorrent
class ResumeDataStorage : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(ResumeDataStorage)
Q_DISABLE_COPY_MOVE(ResumeDataStorage)
public:
using QObject::QObject;

View File

@@ -31,6 +31,7 @@
#include <algorithm>
#include <cstdint>
#include <ctime>
#include <queue>
#include <string>
#include <utility>
@@ -69,7 +70,6 @@
#include <QUuid>
#include "base/algorithm.h"
#include "base/exceptions.h"
#include "base/global.h"
#include "base/logger.h"
#include "base/net/downloadmanager.h"
@@ -89,10 +89,11 @@
#include "common.h"
#include "customstorage.h"
#include "dbresumedatastorage.h"
#include "downloadpriority.h"
#include "filesearcher.h"
#include "filterparserthread.h"
#include "loadtorrentparams.h"
#include "ltunderlyingtype.h"
#include "lttypecast.h"
#include "magneturi.h"
#include "nativesessionextension.h"
#include "portforwarderimpl.h"
@@ -209,7 +210,7 @@ namespace
{
switch (socketType)
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
case lt::socket_type_t::http:
return QLatin1String("HTTP");
case lt::socket_type_t::http_ssl:
@@ -219,7 +220,7 @@ namespace
return QLatin1String("I2P");
case lt::socket_type_t::socks5:
return QLatin1String("SOCKS5");
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
case lt::socket_type_t::socks5_ssl:
return QLatin1String("SOCKS5_SSL");
#endif
@@ -227,7 +228,7 @@ namespace
return QLatin1String("TCP");
case lt::socket_type_t::tcp_ssl:
return QLatin1String("TCP_SSL");
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
case lt::socket_type_t::utp:
return QLatin1String("UTP");
#else
@@ -387,6 +388,7 @@ Session::Session(QObject *parent)
, m_IDNSupportEnabled(BITTORRENT_SESSION_KEY("IDNSupportEnabled"), false)
, m_multiConnectionsPerIpEnabled(BITTORRENT_SESSION_KEY("MultiConnectionsPerIp"), false)
, m_validateHTTPSTrackerCertificate(BITTORRENT_SESSION_KEY("ValidateHTTPSTrackerCertificate"), true)
, m_SSRFMitigationEnabled(BITTORRENT_SESSION_KEY("SSRFMitigation"), true)
, m_blockPeersOnPrivilegedPorts(BITTORRENT_SESSION_KEY("BlockPeersOnPrivilegedPorts"), false)
, m_isAddTrackersEnabled(BITTORRENT_SESSION_KEY("AddTrackersEnabled"), false)
, m_additionalTrackers(BITTORRENT_SESSION_KEY("AdditionalTrackers"))
@@ -407,7 +409,6 @@ Session::Session(QObject *parent)
, m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY("BandwidthSchedulerEnabled"), false)
, m_saveResumeDataInterval(BITTORRENT_SESSION_KEY("SaveResumeDataInterval"), 60)
, m_port(BITTORRENT_SESSION_KEY("Port"), -1)
, m_useRandomPort(BITTORRENT_SESSION_KEY("UseRandomPort"), false)
, m_networkInterface(BITTORRENT_SESSION_KEY("Interface"))
, m_networkInterfaceName(BITTORRENT_SESSION_KEY("InterfaceName"))
, m_networkInterfaceAddress(BITTORRENT_SESSION_KEY("InterfaceAddress"))
@@ -1076,14 +1077,14 @@ void Session::initializeNativeSession()
pack.set_bool(lt::settings_pack::enable_upnp, false);
pack.set_bool(lt::settings_pack::enable_natpmp, false);
#if (LIBTORRENT_VERSION_NUM > 20000)
#ifdef QBT_USES_LIBTORRENT2
// preserve the same behavior as in earlier libtorrent versions
pack.set_bool(lt::settings_pack::enable_set_file_valid_data, true);
#endif
loadLTSettings(pack);
lt::session_params sessionParams {pack, {}};
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
sessionParams.disk_io_constructor = customDiskIOConstructor;
#endif
m_nativeSession = new lt::session {sessionParams};
@@ -1175,7 +1176,7 @@ void Session::initMetrics()
m_metricIndices.disk.diskBlocksInUse = findMetricIndex("disk.disk_blocks_in_use");
m_metricIndices.disk.numBlocksRead = findMetricIndex("disk.num_blocks_read");
#if (LIBTORRENT_VERSION_NUM < 20000)
#ifndef QBT_USES_LIBTORRENT2
m_metricIndices.disk.numBlocksCacheHits = findMetricIndex("disk.num_blocks_cache_hits");
#endif
m_metricIndices.disk.writeJobs = findMetricIndex("disk.num_write_ops");
@@ -1262,7 +1263,7 @@ void Session::loadLTSettings(lt::settings_pack &settingsPack)
settingsPack.set_int(lt::settings_pack::peer_turnover_interval, peerTurnoverInterval());
settingsPack.set_int(lt::settings_pack::aio_threads, asyncIOThreads());
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
settingsPack.set_int(lt::settings_pack::hashing_threads, hashingThreads());
#endif
settingsPack.set_int(lt::settings_pack::file_pool_size, filePoolSize());
@@ -1270,7 +1271,7 @@ void Session::loadLTSettings(lt::settings_pack &settingsPack)
const int checkingMemUsageSize = checkingMemUsage() * 64;
settingsPack.set_int(lt::settings_pack::checking_mem_usage, checkingMemUsageSize);
#if (LIBTORRENT_VERSION_NUM < 20000)
#ifndef QBT_USES_LIBTORRENT2
const int cacheSize = (diskCacheSize() > -1) ? (diskCacheSize() * 64) : -1;
settingsPack.set_int(lt::settings_pack::cache_size, cacheSize);
settingsPack.set_int(lt::settings_pack::cache_expiry, diskCacheTTL());
@@ -1281,7 +1282,7 @@ void Session::loadLTSettings(lt::settings_pack &settingsPack)
settingsPack.set_int(lt::settings_pack::disk_io_read_mode, mode);
settingsPack.set_int(lt::settings_pack::disk_io_write_mode, mode);
#if (LIBTORRENT_VERSION_NUM < 20000)
#ifndef QBT_USES_LIBTORRENT2
settingsPack.set_bool(lt::settings_pack::coalesce_reads, isCoalesceReadWriteEnabled());
settingsPack.set_bool(lt::settings_pack::coalesce_writes, isCoalesceReadWriteEnabled());
#endif
@@ -1381,6 +1382,8 @@ void Session::loadLTSettings(lt::settings_pack &settingsPack)
settingsPack.set_bool(lt::settings_pack::validate_https_trackers, validateHTTPSTrackerCertificate());
settingsPack.set_bool(lt::settings_pack::ssrf_mitigation, isSSRFMitigationEnabled());
settingsPack.set_bool(lt::settings_pack::no_connect_privileged_ports, blockPeersOnPrivilegedPorts());
settingsPack.set_bool(lt::settings_pack::apply_ip_filter_to_trackers, isTrackerFilteringEnabled());
@@ -1421,13 +1424,12 @@ void Session::configureNetworkInterfaces(lt::settings_pack &settingsPack)
if (m_listenInterfaceConfigured)
return;
const int port = useRandomPort() ? 0 : this->port();
if (port > 0) // user specified port
if (port() > 0) // user has specified port number
settingsPack.set_int(lt::settings_pack::max_retry_port_bind, 0);
QStringList endpoints;
QStringList outgoingInterfaces;
const QString portString = ':' + QString::number(port);
const QString portString = ':' + QString::number(port());
for (const QString &ip : asConst(getListeningIPs()))
{
@@ -1489,7 +1491,7 @@ void Session::configurePeerClasses()
// Proactively do the same for 0.0.0.0 and address_v4::any()
f.add_rule(lt::address_v4::any()
, lt::address_v4::broadcast()
, 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::global_peer_class_id));
, 1 << LT::toUnderlyingType(lt::session::global_peer_class_id));
// IPv6 may not be available on OS and the parsing
// would result in an exception -> abnormal program termination
@@ -1498,7 +1500,7 @@ void Session::configurePeerClasses()
{
f.add_rule(lt::address_v6::any()
, lt::make_address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
, 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::global_peer_class_id));
, 1 << LT::toUnderlyingType(lt::session::global_peer_class_id));
}
catch (const std::exception &) {}
@@ -1507,21 +1509,21 @@ void Session::configurePeerClasses()
// local networks
f.add_rule(lt::make_address("10.0.0.0")
, lt::make_address("10.255.255.255")
, 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
, 1 << LT::toUnderlyingType(lt::session::local_peer_class_id));
f.add_rule(lt::make_address("172.16.0.0")
, lt::make_address("172.31.255.255")
, 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
, 1 << LT::toUnderlyingType(lt::session::local_peer_class_id));
f.add_rule(lt::make_address("192.168.0.0")
, lt::make_address("192.168.255.255")
, 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
, 1 << LT::toUnderlyingType(lt::session::local_peer_class_id));
// link local
f.add_rule(lt::make_address("169.254.0.0")
, lt::make_address("169.254.255.255")
, 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
, 1 << LT::toUnderlyingType(lt::session::local_peer_class_id));
// loopback
f.add_rule(lt::make_address("127.0.0.0")
, lt::make_address("127.255.255.255")
, 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
, 1 << LT::toUnderlyingType(lt::session::local_peer_class_id));
// IPv6 may not be available on OS and the parsing
// would result in an exception -> abnormal program termination
@@ -1531,15 +1533,15 @@ void Session::configurePeerClasses()
// link local
f.add_rule(lt::make_address("fe80::")
, lt::make_address("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
, 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
, 1 << LT::toUnderlyingType(lt::session::local_peer_class_id));
// unique local addresses
f.add_rule(lt::make_address("fc00::")
, lt::make_address("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
, 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
, 1 << LT::toUnderlyingType(lt::session::local_peer_class_id));
// loopback
f.add_rule(lt::address_v6::loopback()
, lt::address_v6::loopback()
, 1 << static_cast<LTUnderlyingType<lt::peer_class_t>>(lt::session::local_peer_class_id));
, 1 << LT::toUnderlyingType(lt::session::local_peer_class_id));
}
catch (const std::exception &) {}
}
@@ -1590,7 +1592,7 @@ void Session::populateAdditionalTrackers()
m_additionalTrackerList.clear();
const QString trackers = additionalTrackers();
for (QStringRef tracker : asConst(trackers.splitRef('\n')))
for (QStringView tracker : asConst(QStringView(trackers).split(u'\n')))
{
tracker = tracker.trimmed();
if (!tracker.isEmpty())
@@ -1696,7 +1698,7 @@ void Session::handleDownloadFinished(const Net::DownloadResult &result)
{
case Net::DownloadStatus::Success:
emit downloadFromUrlFinished(result.url);
addTorrent(TorrentInfo::load(result.data), m_downloadedTorrents.take(result.url));
addTorrent(TorrentInfo::load(result.data).value_or(TorrentInfo()), m_downloadedTorrents.take(result.url));
break;
case Net::DownloadStatus::RedirectedToMagnet:
emit downloadFromUrlFinished(result.url);
@@ -1725,8 +1727,10 @@ void Session::fileSearchFinished(const TorrentID &id, const QString &savePath, c
lt::add_torrent_params &p = params.ltAddTorrentParams;
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
const TorrentInfo torrentInfo {p.ti};
const auto nativeIndexes = torrentInfo.nativeIndexes();
for (int i = 0; i < fileNames.size(); ++i)
p.renamed_files[lt::file_index_t {i}] = fileNames[i].toStdString();
p.renamed_files[nativeIndexes[i]] = fileNames[i].toStdString();
loadTorrent(params);
}
@@ -2024,7 +2028,7 @@ bool Session::addTorrent(const QString &source, const AddTorrentParams &params)
return addTorrent(magnetUri, params);
TorrentFileGuard guard {source};
if (addTorrent(TorrentInfo::loadFromFile(source), params))
if (addTorrent(TorrentInfo::loadFromFile(source).value_or(TorrentInfo()), params))
{
guard.markAsAddedToSession();
return true;
@@ -2139,19 +2143,20 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
{
QString contentName = metadata.rootFolder();
if (contentName.isEmpty() && (metadata.filesCount() == 1))
contentName = metadata.fileName(0);
contentName = Utils::Fs::fileName(metadata.filePath(0));
if (!contentName.isEmpty() && (contentName != metadata.name()))
loadTorrentParams.name = contentName;
}
Q_ASSERT(p.file_priorities.empty());
std::transform(addTorrentParams.filePriorities.cbegin(), addTorrentParams.filePriorities.cend()
, std::back_inserter(p.file_priorities), [](const DownloadPriority priority)
{
return static_cast<lt::download_priority_t>(
static_cast<lt::download_priority_t::underlying_type>(priority));
});
const int internalFilesCount = metadata.nativeInfo()->files().num_files(); // including .pad files
// Use qBittorrent default priority rather than libtorrent's (4)
p.file_priorities = std::vector(internalFilesCount, LT::toNative(DownloadPriority::Normal));
const auto nativeIndexes = metadata.nativeIndexes();
Q_ASSERT(addTorrentParams.filePriorities.isEmpty() || (addTorrentParams.filePriorities.size() == nativeIndexes.size()));
for (int i = 0; i < addTorrentParams.filePriorities.size(); ++i)
p.file_priorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(addTorrentParams.filePriorities[i]);
p.ti = metadata.nativeInfo();
}
@@ -2192,6 +2197,8 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
else
p.flags |= lt::torrent_flags::auto_managed;
p.added_time = std::time(nullptr);
if (!isFindingIncompleteFiles)
return loadTorrent(loadTorrentParams);
@@ -2204,7 +2211,7 @@ bool Session::loadTorrent(LoadTorrentParams params)
{
lt::add_torrent_params &p = params.ltAddTorrentParams;
#if (LIBTORRENT_VERSION_NUM < 20000)
#ifndef QBT_USES_LIBTORRENT2
p.storage = customStorageConstructor;
#endif
// Limits
@@ -2212,7 +2219,7 @@ bool Session::loadTorrent(LoadTorrentParams params)
p.max_uploads = maxUploadsPerTorrent();
const bool hasMetadata = (p.ti && p.ti->is_valid());
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto id = TorrentID::fromInfoHash(hasMetadata ? p.ti->info_hashes() : p.info_hashes);
#else
const auto id = TorrentID::fromInfoHash(hasMetadata ? p.ti->info_hash() : p.info_hash);
@@ -2279,7 +2286,7 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri)
// Solution to avoid accidental file writes
p.flags |= lt::torrent_flags::upload_mode;
#if (LIBTORRENT_VERSION_NUM < 20000)
#ifndef QBT_USES_LIBTORRENT2
p.storage = customStorageConstructor;
#endif
@@ -2296,33 +2303,27 @@ bool Session::downloadMetadata(const MagnetUri &magnetUri)
return true;
}
void Session::exportTorrentFile(const Torrent *torrent, TorrentExportFolder folder)
void Session::exportTorrentFile(const TorrentInfo &torrentInfo, const QString &folderPath, const QString &baseName)
{
Q_ASSERT(((folder == TorrentExportFolder::Regular) && !torrentExportDirectory().isEmpty()) ||
((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty()));
const QString validName = Utils::Fs::toValidFileSystemName(torrent->name());
const QString validName = Utils::Fs::toValidFileSystemName(baseName);
QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
const QDir exportPath(folder == TorrentExportFolder::Regular ? torrentExportDirectory() : finishedTorrentExportDirectory());
if (exportPath.exists() || exportPath.mkpath(exportPath.absolutePath()))
const QDir exportDir {folderPath};
if (exportDir.exists() || exportDir.mkpath(exportDir.absolutePath()))
{
QString newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
QString newTorrentPath = exportDir.absoluteFilePath(torrentExportFilename);
int counter = 0;
while (QFile::exists(newTorrentPath))
{
// Append number to torrent name to make it unique
torrentExportFilename = QString::fromLatin1("%1 %2.torrent").arg(validName).arg(++counter);
newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
newTorrentPath = exportDir.absoluteFilePath(torrentExportFilename);
}
try
{
torrent->info().saveToFile(newTorrentPath);
}
catch (const RuntimeError &err)
const nonstd::expected<void, QString> result = torrentInfo.saveToFile(newTorrentPath);
if (!result)
{
LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2.")
.arg(newTorrentPath, err.message()), Log::WARNING);
.arg(newTorrentPath, result.error()), Log::WARNING);
}
}
}
@@ -2380,7 +2381,7 @@ void Session::saveTorrentsQueue() const
for (const TorrentImpl *torrent : asConst(m_torrents))
{
// We require actual (non-cached) queue position here!
const int queuePos = static_cast<LTUnderlyingType<lt::queue_position_t>>(torrent->nativeHandle().queue_position());
const int queuePos = LT::toUnderlyingType(torrent->nativeHandle().queue_position());
if (queuePos >= 0)
{
if (queuePos >= queue.size())
@@ -2525,7 +2526,7 @@ QStringList Session::getListeningIPs() const
}
const QList<QNetworkAddressEntry> addresses = networkIFace.addressEntries();
qDebug("This network interface has %d IP addresses", addresses.size());
qDebug() << "This network interface has " << addresses.size() << " IP addresses";
for (const QNetworkAddressEntry &entry : addresses)
checkAndAddIP(entry.ip(), configuredAddr);
@@ -2754,16 +2755,6 @@ void Session::setPort(const int port)
}
}
bool Session::useRandomPort() const
{
return m_useRandomPort;
}
void Session::setUseRandomPort(const bool value)
{
m_useRandomPort = value;
}
QString Session::networkInterface() const
{
return m_networkInterface;
@@ -2931,7 +2922,7 @@ void Session::setBannedIPs(const QStringList &newList)
}
else
{
LogMsg(tr("%1 is not a valid IP address and was rejected while applying the list of banned addresses.")
LogMsg(tr("%1 is not a valid IP address and was rejected while applying the list of banned IP addresses.")
.arg(ip)
, Log::WARNING);
}
@@ -3753,6 +3744,19 @@ void Session::setValidateHTTPSTrackerCertificate(const bool enabled)
configureDeferred();
}
bool Session::isSSRFMitigationEnabled() const
{
return m_SSRFMitigationEnabled;
}
void Session::setSSRFMitigationEnabled(const bool enabled)
{
if (enabled == m_SSRFMitigationEnabled) return;
m_SSRFMitigationEnabled = enabled;
configureDeferred();
}
bool Session::blockPeersOnPrivilegedPorts() const
{
return m_blockPeersOnPrivilegedPorts;
@@ -3893,7 +3897,14 @@ void Session::handleTorrentMetadataReceived(TorrentImpl *const torrent)
{
// Copy the torrent file to the export folder
if (!torrentExportDirectory().isEmpty())
exportTorrentFile(torrent);
{
#ifdef QBT_USES_LIBTORRENT2
const TorrentInfo torrentInfo {torrent->nativeHandle().torrent_file_with_hashes()};
#else
const TorrentInfo torrentInfo {torrent->nativeHandle().torrent_file()};
#endif
exportTorrentFile(torrentInfo, torrentExportDirectory(), torrent->name());
}
emit torrentMetadataReceived(torrent);
}
@@ -3919,16 +3930,14 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent)
qDebug("Checking if the torrent contains torrent files to download");
// Check if there are torrent files inside
for (int i = 0; i < torrent->filesCount(); ++i)
for (const QString &torrentRelpath : asConst(torrent->filePaths()))
{
const QString torrentRelpath = torrent->filePath(i);
if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive))
{
qDebug("Found possible recursive torrent download.");
const QString torrentFullpath = torrent->savePath(true) + '/' + torrentRelpath;
qDebug("Full subtorrent path is %s", qUtf8Printable(torrentFullpath));
TorrentInfo torrentInfo = TorrentInfo::loadFromFile(torrentFullpath);
if (torrentInfo.isValid())
if (TorrentInfo::loadFromFile(torrentFullpath))
{
qDebug("emitting recursiveTorrentDownloadPossible()");
emit recursiveTorrentDownloadPossible(torrent);
@@ -3944,7 +3953,14 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent)
// Move .torrent file to another folder
if (!finishedTorrentExportDirectory().isEmpty())
exportTorrentFile(torrent, TorrentExportFolder::Finished);
{
#ifdef QBT_USES_LIBTORRENT2
const TorrentInfo torrentInfo {torrent->nativeHandle().torrent_file_with_hashes()};
#else
const TorrentInfo torrentInfo {torrent->nativeHandle().torrent_file()};
#endif
exportTorrentFile(torrentInfo, finishedTorrentExportDirectory(), torrent->name());
}
if (!hasUnfinishedTorrents())
emit allTorrentsFinished();
@@ -4023,7 +4039,7 @@ bool Session::addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newP
void Session::moveTorrentStorage(const MoveStorageJob &job) const
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto id = TorrentID::fromInfoHash(job.torrentHandle.info_hashes());
#else
const auto id = TorrentID::fromInfoHash(job.torrentHandle.info_hash());
@@ -4137,9 +4153,8 @@ void Session::recursiveTorrentDownload(const TorrentID &id)
TorrentImpl *const torrent = m_torrents.value(id);
if (!torrent) return;
for (int i = 0; i < torrent->filesCount(); ++i)
for (const QString &torrentRelpath : asConst(torrent->filePaths()))
{
const QString torrentRelpath = torrent->filePath(i);
if (torrentRelpath.endsWith(".torrent"))
{
LogMsg(tr("Recursive download of file '%1' embedded in torrent '%2'"
@@ -4150,7 +4165,7 @@ void Session::recursiveTorrentDownload(const TorrentID &id)
AddTorrentParams params;
// Passing the save path along to the sub torrent file
params.savePath = torrent->savePath();
addTorrent(TorrentInfo::loadFromFile(torrentFullpath), params);
addTorrent(TorrentInfo::loadFromFile(torrentFullpath).value_or(TorrentInfo()), params);
}
}
}
@@ -4322,7 +4337,7 @@ void Session::handleAlert(const lt::alert *a)
{
switch (a->type())
{
#if (LIBTORRENT_VERSION_NUM >= 20003)
#ifdef QBT_USES_LIBTORRENT2
case lt::file_prio_alert::alert_type:
#endif
case lt::file_renamed_alert::alert_type:
@@ -4424,7 +4439,7 @@ void Session::dispatchTorrentAlert(const lt::alert *a)
void Session::createTorrent(const lt::torrent_handle &nativeHandle)
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto torrentID = TorrentID::fromInfoHash(nativeHandle.info_hashes());
#else
const auto torrentID = TorrentID::fromInfoHash(nativeHandle.info_hash());
@@ -4452,7 +4467,10 @@ void Session::createTorrent(const lt::torrent_handle &nativeHandle)
{
// Copy the torrent file to the export folder
if (!torrentExportDirectory().isEmpty())
exportTorrentFile(torrent);
{
const TorrentInfo torrentInfo {params.ltAddTorrentParams.ti};
exportTorrentFile(torrentInfo, torrentExportDirectory(), torrent->name());
}
}
if (isAddTrackersEnabled() && !torrent->isPrivate())
@@ -4487,7 +4505,7 @@ void Session::handleAddTorrentAlert(const lt::add_torrent_alert *p)
const lt::add_torrent_params &params = p->params;
const bool hasMetadata = (params.ti && params.ti->is_valid());
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto id = TorrentID::fromInfoHash(hasMetadata ? params.ti->info_hashes() : params.info_hashes);
#else
const auto id = TorrentID::fromInfoHash(hasMetadata ? params.ti->info_hash() : params.info_hash);
@@ -4502,7 +4520,7 @@ void Session::handleAddTorrentAlert(const lt::add_torrent_alert *p)
void Session::handleTorrentRemovedAlert(const lt::torrent_removed_alert *p)
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto id = TorrentID::fromInfoHash(p->info_hashes);
#else
const auto id = TorrentID::fromInfoHash(p->info_hash);
@@ -4521,7 +4539,7 @@ void Session::handleTorrentRemovedAlert(const lt::torrent_removed_alert *p)
void Session::handleTorrentDeletedAlert(const lt::torrent_deleted_alert *p)
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto id = TorrentID::fromInfoHash(p->info_hashes);
#else
const auto id = TorrentID::fromInfoHash(p->info_hash);
@@ -4539,7 +4557,7 @@ void Session::handleTorrentDeletedAlert(const lt::torrent_deleted_alert *p)
void Session::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_alert *p)
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto id = TorrentID::fromInfoHash(p->info_hashes);
#else
const auto id = TorrentID::fromInfoHash(p->info_hash);
@@ -4569,7 +4587,7 @@ void Session::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_ale
void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto id = TorrentID::fromInfoHash(p->handle.info_hashes());
#else
const auto id = TorrentID::fromInfoHash(p->handle.info_hash());
@@ -4579,7 +4597,7 @@ void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
if (downloadedMetadataIter != m_downloadedMetadata.end())
{
TorrentInfo metadata {p->handle.torrent_file()};
const TorrentInfo metadata {p->handle.torrent_file()};
m_downloadedMetadata.erase(downloadedMetadataIter);
--m_extraLimit;
@@ -4772,7 +4790,7 @@ void Session::handleSessionStatsAlert(const lt::session_stats_alert *p)
m_cacheStatus.totalUsedBuffers = stats[m_metricIndices.disk.diskBlocksInUse];
m_cacheStatus.jobQueueLength = stats[m_metricIndices.disk.queuedDiskJobs];
#if (LIBTORRENT_VERSION_NUM < 20000)
#ifndef QBT_USES_LIBTORRENT2
const int64_t numBlocksRead = stats[m_metricIndices.disk.numBlocksRead];
const int64_t numBlocksCacheHits = stats[m_metricIndices.disk.numBlocksCacheHits];
m_cacheStatus.readRatio = static_cast<qreal>(numBlocksCacheHits) / std::max<int64_t>((numBlocksCacheHits + numBlocksRead), 1);
@@ -4807,7 +4825,7 @@ void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
const QString newPath {p->storage_path()};
Q_ASSERT(newPath == currentJob.path);
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hashes());
#else
const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hash());
@@ -4827,7 +4845,7 @@ void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert
const MoveStorageJob &currentJob = m_moveStorageQueue.first();
Q_ASSERT(currentJob.torrentHandle == p->handle);
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hashes());
#else
const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hash());
@@ -4850,7 +4868,7 @@ void Session::handleStateUpdateAlert(const lt::state_update_alert *p)
for (const lt::torrent_status &status : p->status)
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const auto id = TorrentID::fromInfoHash(status.info_hashes);
#else
const auto id = TorrentID::fromInfoHash(status.info_hash);

View File

@@ -36,7 +36,6 @@
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/fwd.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/version.hpp>
#include <QHash>
#include <QPointer>
@@ -82,12 +81,6 @@ enum DeleteOption
DeleteTorrentAndFiles
};
enum TorrentExportFolder
{
Regular,
Finished
};
namespace Net
{
struct DownloadResult;
@@ -197,7 +190,7 @@ namespace BitTorrent
{
int diskBlocksInUse = -1;
int numBlocksRead = -1;
#if (LIBTORRENT_VERSION_NUM < 20000)
#ifndef QBT_USES_LIBTORRENT2
int numBlocksCacheHits = -1;
#endif
int writeJobs = -1;
@@ -211,7 +204,7 @@ namespace BitTorrent
class Session : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Session)
Q_DISABLE_COPY_MOVE(Session)
public:
static void initInstance();
@@ -312,8 +305,6 @@ namespace BitTorrent
void setSaveResumeDataInterval(int value);
int port() const;
void setPort(int port);
bool useRandomPort() const;
void setUseRandomPort(bool value);
QString networkInterface() const;
void setNetworkInterface(const QString &iface);
QString networkInterfaceName() const;
@@ -435,6 +426,8 @@ namespace BitTorrent
void setMultiConnectionsPerIpEnabled(bool enabled);
bool validateHTTPSTrackerCertificate() const;
void setValidateHTTPSTrackerCertificate(bool enabled);
bool isSSRFMitigationEnabled() const;
void setSSRFMitigationEnabled(bool enabled);
bool blockPeersOnPrivilegedPorts() const;
void setBlockPeersOnPrivilegedPorts(bool enabled);
bool isTrackerFilteringEnabled() const;
@@ -613,7 +606,7 @@ namespace BitTorrent
bool addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source, const AddTorrentParams &addTorrentParams);
void updateSeedingLimitTimer();
void exportTorrentFile(const Torrent *torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
void exportTorrentFile(const TorrentInfo &torrentInfo, const QString &folderPath, const QString &baseName);
void handleAlert(const lt::alert *a);
void dispatchTorrentAlert(const lt::alert *a);
@@ -708,6 +701,7 @@ namespace BitTorrent
CachedSettingValue<bool> m_IDNSupportEnabled;
CachedSettingValue<bool> m_multiConnectionsPerIpEnabled;
CachedSettingValue<bool> m_validateHTTPSTrackerCertificate;
CachedSettingValue<bool> m_SSRFMitigationEnabled;
CachedSettingValue<bool> m_blockPeersOnPrivilegedPorts;
CachedSettingValue<bool> m_isAddTrackersEnabled;
CachedSettingValue<QString> m_additionalTrackers;
@@ -728,7 +722,6 @@ namespace BitTorrent
CachedSettingValue<bool> m_isBandwidthSchedulerEnabled;
CachedSettingValue<int> m_saveResumeDataInterval;
CachedSettingValue<int> m_port;
CachedSettingValue<bool> m_useRandomPort;
CachedSettingValue<QString> m_networkInterface;
CachedSettingValue<QString> m_networkInterfaceName;
CachedSettingValue<QString> m_networkInterfaceAddress;

View File

@@ -38,34 +38,30 @@
template<typename T>
struct Sample
{
Sample()
: download()
, upload()
constexpr Sample() = default;
constexpr Sample(const T dl, const T ul)
: download {dl}
, upload {ul}
{
}
Sample(T dl, T ul)
: download(dl)
, upload(ul)
{
}
Sample<T> &operator+=(const Sample<T> &other)
constexpr Sample<T> &operator+=(const Sample<T> &other)
{
download += other.download;
upload += other.upload;
upload += other.upload;
return *this;
}
Sample<T> &operator-=(const Sample<T> &other)
constexpr Sample<T> &operator-=(const Sample<T> &other)
{
download -= other.download;
upload -= other.upload;
upload -= other.upload;
return *this;
}
T download;
T upload;
T download {};
T upload {};
};
typedef Sample<qlonglong> SpeedSample;

View File

@@ -39,7 +39,7 @@ namespace BitTorrent
class Statistics : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Statistics)
Q_DISABLE_COPY_MOVE(Statistics)
public:
explicit Statistics(BitTorrent::Session *session);

View File

@@ -72,6 +72,7 @@ namespace BitTorrent
ForcedDownloading,
Downloading,
ForcedDownloadingMetadata,
DownloadingMetadata,
StalledDownloading,
@@ -192,6 +193,7 @@ namespace BitTorrent
virtual qreal ratioLimit() const = 0;
virtual int seedingTimeLimit() const = 0;
virtual QStringList filePaths() const = 0;
virtual QStringList absoluteFilePaths() const = 0;
virtual QVector<DownloadPriority> filePriorities() const = 0;
@@ -213,7 +215,6 @@ namespace BitTorrent
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 QVector<QUrl> urlSeeds() const = 0;

View File

@@ -30,13 +30,11 @@
#include <fstream>
#include <libtorrent/bencode.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/file_storage.hpp>
#include <libtorrent/torrent_info.hpp>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QHash>
@@ -46,7 +44,7 @@
#include "base/utils/fs.h"
#include "base/utils/io.h"
#include "base/version.h"
#include "ltunderlyingtype.h"
#include "lttypecast.h"
namespace
{
@@ -57,7 +55,7 @@ namespace
return !Utils::Fs::fileName(QString::fromStdString(f)).startsWith('.');
}
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
lt::create_flags_t toNativeTorrentFormatFlag(const BitTorrent::TorrentFormat torrentFormat)
{
switch (torrentFormat)
@@ -159,7 +157,7 @@ void TorrentCreatorThread::run()
checkInterruptionRequested();
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
lt::create_torrent newTorrent {fs, m_params.pieceSize, toNativeTorrentFormatFlag(m_params.torrentFormat)};
#else
lt::create_torrent newTorrent(fs, m_params.pieceSize, m_params.paddedFileSizeLimit
@@ -188,7 +186,7 @@ void TorrentCreatorThread::run()
, [this, &newTorrent](const lt::piece_index_t n)
{
checkInterruptionRequested();
sendProgressSignal(static_cast<LTUnderlyingType<lt::piece_index_t>>(n), newTorrent.num_pieces());
sendProgressSignal(LT::toUnderlyingType(n), newTorrent.num_pieces());
});
// Set qBittorrent as creator and add user comment to
@@ -209,16 +207,9 @@ void TorrentCreatorThread::run()
checkInterruptionRequested();
// create the torrent
QFile outfile {m_params.savePath};
if (!outfile.open(QIODevice::WriteOnly))
throw RuntimeError(outfile.errorString());
checkInterruptionRequested();
lt::bencode(Utils::IO::FileDeviceOutputIterator {outfile}, entry);
if (outfile.error() != QFileDevice::NoError)
throw RuntimeError(outfile.errorString());
outfile.close();
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(m_params.savePath, entry);
if (!result)
throw RuntimeError(result.error());
emit updateProgress(100);
emit creationSuccess(m_params.savePath, parentPath);
@@ -233,7 +224,7 @@ void TorrentCreatorThread::run()
}
}
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize, const TorrentFormat torrentFormat)
#else
int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize, const bool isAlignmentOptimized, const int paddedFileSizeLimit)
@@ -245,7 +236,7 @@ int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const i
lt::file_storage fs;
lt::add_files(fs, Utils::Fs::toNativePath(inputPath).toStdString(), fileFilter);
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
return lt::create_torrent {fs, pieceSize, toNativeTorrentFormatFlag(torrentFormat)}.num_pieces();
#else
return lt::create_torrent(fs, pieceSize, paddedFileSizeLimit

View File

@@ -28,14 +28,12 @@
#pragma once
#include <libtorrent/version.hpp>
#include <QStringList>
#include <QThread>
namespace BitTorrent
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
enum class TorrentFormat
{
V1,
@@ -47,7 +45,7 @@ namespace BitTorrent
struct TorrentCreatorParams
{
bool isPrivate;
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
TorrentFormat torrentFormat;
#else
bool isAlignmentOptimized;
@@ -73,7 +71,7 @@ namespace BitTorrent
void create(const TorrentCreatorParams &params);
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
static int calculateTotalPieces(const QString &inputPath, const int pieceSize, const TorrentFormat torrentFormat);
#else
static int calculateTotalPieces(const QString &inputPath

View File

@@ -43,9 +43,8 @@
#include <libtorrent/session.hpp>
#include <libtorrent/storage_defs.hpp>
#include <libtorrent/time.hpp>
#include <libtorrent/version.hpp>
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
#include <libtorrent/info_hash.hpp>
#endif
@@ -65,7 +64,7 @@
#include "downloadpriority.h"
#include "loadtorrentparams.h"
#include "ltqhash.h"
#include "ltunderlyingtype.h"
#include "lttypecast.h"
#include "peeraddress.h"
#include "peerinfo.h"
#include "session.h"
@@ -75,20 +74,6 @@ using namespace BitTorrent;
namespace
{
std::vector<lt::download_priority_t> toLTDownloadPriorities(const QVector<DownloadPriority> &priorities)
{
std::vector<lt::download_priority_t> out;
out.reserve(priorities.size());
std::transform(priorities.cbegin(), priorities.cend()
, std::back_inserter(out), [](const DownloadPriority priority)
{
return static_cast<lt::download_priority_t>(
static_cast<LTUnderlyingType<lt::download_priority_t>>(priority));
});
return out;
}
lt::announce_entry makeNativeAnnouncerEntry(const QString &url, const int tier)
{
lt::announce_entry entry {url.toStdString()};
@@ -96,7 +81,7 @@ namespace
return entry;
}
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry
, const lt::info_hash_t &hashes, const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
#else
@@ -111,8 +96,8 @@ namespace
int numNotWorking = 0;
QString firstTrackerMessage;
QString firstErrorMessage;
#if (LIBTORRENT_VERSION_NUM >= 20000)
const size_t numEndpoints = nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1);
#ifdef QBT_USES_LIBTORRENT2
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1));
trackerEntry.endpoints.reserve(static_cast<decltype(trackerEntry.endpoints)::size_type>(numEndpoints));
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
{
@@ -167,8 +152,8 @@ namespace
}
}
#else
const int numEndpoints = nativeEntry.endpoints.size();
trackerEntry.endpoints.reserve(numEndpoints);
const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size());
trackerEntry.endpoints.reserve(static_cast<decltype(trackerEntry.endpoints)::size_type>(numEndpoints));
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
{
TrackerEntry::EndpointStats trackerEndpoint;
@@ -265,7 +250,7 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
, m_session(session)
, m_nativeSession(nativeSession)
, m_nativeHandle(nativeHandle)
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
, m_infoHash(m_nativeHandle.info_hashes())
#else
, m_infoHash(m_nativeHandle.info_hash())
@@ -478,7 +463,7 @@ QVector<TrackerEntry> TorrentImpl::trackers() const
for (const lt::announce_entry &tracker : nativeTrackers)
{
const QString trackerURL = QString::fromStdString(tracker.url);
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
entries << fromNativeAnnouncerEntry(tracker, m_nativeHandle.info_hashes(), m_trackerPeerCounts[trackerURL]);
#else
entries << fromNativeAnnouncerEntry(tracker, m_trackerPeerCounts[trackerURL]);
@@ -763,30 +748,30 @@ int TorrentImpl::seedingTimeLimit() const
return m_seedingTimeLimit;
}
QString TorrentImpl::filePath(int index) const
QString TorrentImpl::filePath(const int index) const
{
return m_torrentInfo.filePath(index);
}
QString TorrentImpl::fileName(int index) const
{
if (!hasMetadata()) return {};
return Utils::Fs::fileName(filePath(index));
}
qlonglong TorrentImpl::fileSize(int index) const
qlonglong TorrentImpl::fileSize(const int index) const
{
return m_torrentInfo.fileSize(index);
}
QStringList TorrentImpl::filePaths() const
{
return m_torrentInfo.filePaths();
}
// Return a list of absolute paths corresponding
// to all files in a torrent
QStringList TorrentImpl::absoluteFilePaths() const
{
if (!hasMetadata()) return {};
const QDir saveDir(savePath(true));
const QDir saveDir {savePath(true)};
QStringList res;
res.reserve(filesCount());
for (int i = 0; i < filesCount(); ++i)
res << Utils::Fs::expandPathAbs(saveDir.absoluteFilePath(filePath(i)));
return res;
@@ -794,13 +779,19 @@ QStringList TorrentImpl::absoluteFilePaths() const
QVector<DownloadPriority> TorrentImpl::filePriorities() const
{
if (!hasMetadata())
return {};
const std::vector<lt::download_priority_t> fp = m_nativeHandle.get_file_priorities();
QVector<DownloadPriority> ret;
std::transform(fp.cbegin(), fp.cend(), std::back_inserter(ret), [](lt::download_priority_t priority)
ret.reserve(filesCount());
for (const lt::file_index_t nativeIndex : asConst(m_torrentInfo.nativeIndexes()))
{
return static_cast<DownloadPriority>(static_cast<LTUnderlyingType<lt::download_priority_t>>(priority));
});
const auto priority = LT::fromNative(fp[LT::toUnderlyingType(nativeIndex)]);
ret.append(priority);
}
return ret;
}
@@ -832,6 +823,7 @@ bool TorrentImpl::isDownloading() const
{
return m_state == TorrentState::Downloading
|| m_state == TorrentState::DownloadingMetadata
|| m_state == TorrentState::ForcedDownloadingMetadata
|| m_state == TorrentState::StalledDownloading
|| m_state == TorrentState::CheckingDownloading
|| m_state == TorrentState::PausedDownloading
@@ -864,6 +856,7 @@ bool TorrentImpl::isActive() const
return (uploadPayloadRate() > 0);
return m_state == TorrentState::DownloadingMetadata
|| m_state == TorrentState::ForcedDownloadingMetadata
|| m_state == TorrentState::Downloading
|| m_state == TorrentState::ForcedDownloading
|| m_state == TorrentState::Uploading
@@ -933,7 +926,7 @@ void TorrentImpl::updateState()
else if (m_session->isQueueingSystemEnabled() && isQueued())
m_state = TorrentState::QueuedDownloading;
else
m_state = TorrentState::DownloadingMetadata;
m_state = isForced() ? TorrentState::ForcedDownloadingMetadata : TorrentState::DownloadingMetadata;
}
else if ((m_nativeStatus.state == lt::torrent_status::checking_files)
&& (!isPaused() || (m_nativeStatus.flags & lt::torrent_flags::auto_managed)
@@ -985,15 +978,6 @@ bool TorrentImpl::hasError() const
return (m_nativeStatus.errc || (m_nativeStatus.flags & lt::torrent_flags::upload_mode));
}
bool TorrentImpl::hasFilteredPieces() const
{
const std::vector<lt::download_priority_t> pp = m_nativeHandle.get_piece_priorities();
return std::any_of(pp.cbegin(), pp.cend(), [](const lt::download_priority_t priority)
{
return (priority == lt::download_priority_t {0});
});
}
int TorrentImpl::queuePosition() const
{
return static_cast<int>(m_nativeStatus.queue_position);
@@ -1090,16 +1074,18 @@ QVector<qreal> TorrentImpl::filesProgress() const
std::vector<int64_t> fp;
m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity);
const int count = static_cast<int>(fp.size());
const int count = filesCount();
const auto nativeIndexes = m_torrentInfo.nativeIndexes();
QVector<qreal> result;
result.reserve(count);
for (int i = 0; i < count; ++i)
{
const int64_t progress = fp[LT::toUnderlyingType(nativeIndexes[i])];
const qlonglong size = fileSize(i);
if ((size <= 0) || (fp[i] == size))
if ((size <= 0) || (progress == size))
result << 1;
else
result << (fp[i] / static_cast<qreal>(size));
result << (progress / static_cast<qreal>(size));
}
return result;
@@ -1250,7 +1236,7 @@ QBitArray TorrentImpl::downloadingPieces() const
m_nativeHandle.get_download_queue(queue);
for (const lt::partial_piece_info &info : queue)
result.setBit(static_cast<LTUnderlyingType<lt::piece_index_t>>(info.piece_index));
result.setBit(LT::toUnderlyingType(info.piece_index));
return result;
}
@@ -1404,7 +1390,7 @@ void TorrentImpl::move_impl(QString path, const MoveStorageMode mode)
}
}
void TorrentImpl::forceReannounce(int index)
void TorrentImpl::forceReannounce(const int index)
{
m_nativeHandle.force_reannounce(0, index);
}
@@ -1467,28 +1453,29 @@ void TorrentImpl::applyFirstLastPiecePriority(const bool enabled, const QVector<
// Download first and last pieces first for every file in the torrent
const std::vector<lt::download_priority_t> filePriorities = !updatedFilePrio.isEmpty() ? toLTDownloadPriorities(updatedFilePrio)
: nativeHandle().get_file_priorities();
const QVector<DownloadPriority> filePriorities =
!updatedFilePrio.isEmpty() ? updatedFilePrio : this->filePriorities();
std::vector<lt::download_priority_t> piecePriorities = nativeHandle().get_piece_priorities();
// Updating file priorities is an async operation in libtorrent, when we just updated it and immediately query it
// we might get the old/wrong values, so we rely on `updatedFilePrio` in this case.
for (int index = 0; index < static_cast<int>(filePriorities.size()); ++index)
for (int index = 0; index < filePriorities.size(); ++index)
{
const lt::download_priority_t filePrio = filePriorities[index];
if (filePrio <= lt::download_priority_t {0})
const DownloadPriority filePrio = filePriorities[index];
if (filePrio <= DownloadPriority::Ignored)
continue;
// Determine the priority to set
const lt::download_priority_t newPrio = enabled ? lt::download_priority_t {7} : filePrio;
const DownloadPriority newPrio = enabled ? DownloadPriority::Maximum : filePrio;
const auto piecePrio = static_cast<lt::download_priority_t>(static_cast<int>(newPrio));
const TorrentInfo::PieceRange extremities = info().filePieces(index);
// worst case: AVI index = 1% of total file size (at the end of the file)
const int nNumPieces = std::ceil(fileSize(index) * 0.01 / pieceLength());
for (int i = 0; i < nNumPieces; ++i)
{
piecePriorities[extremities.first() + i] = newPrio;
piecePriorities[extremities.last() - i] = newPrio;
piecePriorities[extremities.first() + i] = piecePrio;
piecePriorities[extremities.last() - i] = piecePrio;
}
}
@@ -1504,10 +1491,16 @@ void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QSt
{
lt::add_torrent_params &p = m_ltAddTorrentParams;
p.ti = std::const_pointer_cast<lt::torrent_info>(m_nativeHandle.torrent_file());
const TorrentInfo torrentInfo {m_nativeHandle.torrent_file()};
const auto nativeIndexes = torrentInfo.nativeIndexes();
for (int i = 0; i < fileNames.size(); ++i)
p.renamed_files[lt::file_index_t {i}] = fileNames[i].toStdString();
p.renamed_files[nativeIndexes[i]] = fileNames[i].toStdString();
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
p.ti = torrentInfo.nativeInfo();
const int internalFilesCount = p.ti->files().num_files(); // including .pad files
// Use qBittorrent default priority rather than libtorrent's (4)
p.file_priorities = std::vector(internalFilesCount, LT::toNative(DownloadPriority::Normal));
reload();
@@ -1622,10 +1615,12 @@ void TorrentImpl::moveStorage(const QString &newPath, const MoveStorageMode mode
void TorrentImpl::renameFile(const int index, const QString &path)
{
#ifndef QBT_USES_LIBTORRENT2
const QString oldPath = filePath(index);
m_oldPath[lt::file_index_t {index}].push_back(oldPath);
m_oldPath[index].push_back(oldPath);
#endif
++m_renameCount;
m_nativeHandle.rename_file(lt::file_index_t {index}, Utils::Fs::toNativePath(path).toStdString());
m_nativeHandle.rename_file(m_torrentInfo.nativeIndexes()[index], Utils::Fs::toNativePath(path).toStdString());
}
void TorrentImpl::handleStateUpdate(const lt::torrent_status &nativeStatus)
@@ -1809,8 +1804,6 @@ void TorrentImpl::prepareResumeData(const lt::add_torrent_params &params)
m_ltAddTorrentParams = params;
}
m_ltAddTorrentParams.added_time = addedTime().toSecsSinceEpoch();
// We shouldn't save upload_mode flag to allow torrent operate normally on next run
m_ltAddTorrentParams.flags &= ~lt::torrent_flags::upload_mode;
@@ -1856,18 +1849,24 @@ void TorrentImpl::handleFastResumeRejectedAlert(const lt::fastresume_rejected_al
void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
{
const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index);
Q_ASSERT(fileIndex >= 0);
// Remove empty leftover folders
// For example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will
// be removed if they are empty
const QString oldFilePath = m_oldPath[p->index].takeFirst();
#ifndef QBT_USES_LIBTORRENT2
const QString oldFilePath = m_oldPath[fileIndex].takeFirst();
if (m_oldPath[fileIndex].isEmpty())
m_oldPath.remove(fileIndex);
#else
const QString oldFilePath = Utils::Fs::toUniformPath(p->old_name());
#endif
const QString newFilePath = Utils::Fs::toUniformPath(p->new_name());
if (m_oldPath[p->index].isEmpty())
m_oldPath.remove(p->index);
QVector<QStringRef> oldPathParts = oldFilePath.splitRef('/', Qt::SkipEmptyParts);
QList<QStringView> oldPathParts = QStringView(oldFilePath).split('/', Qt::SkipEmptyParts);
oldPathParts.removeLast(); // drop file name part
QVector<QStringRef> newPathParts = newFilePath.splitRef('/', Qt::SkipEmptyParts);
QList<QStringView> newPathParts = QStringView(newFilePath).split('/', Qt::SkipEmptyParts);
newPathParts.removeLast(); // drop file name part
#if defined(Q_OS_WIN)
@@ -1886,7 +1885,7 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
for (int i = (oldPathParts.size() - 1); i >= pathIdx; --i)
{
QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QLatin1String("/")));
QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QString::fromLatin1("/")));
oldPathParts.removeLast();
}
@@ -1899,13 +1898,17 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p)
{
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
.arg(name(), filePath(static_cast<LTUnderlyingType<lt::file_index_t>>(p->index))
, QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index);
Q_ASSERT(fileIndex >= 0);
m_oldPath[p->index].removeFirst();
if (m_oldPath[p->index].isEmpty())
m_oldPath.remove(p->index);
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
.arg(name(), filePath(fileIndex), QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
#ifndef QBT_USES_LIBTORRENT2
m_oldPath[fileIndex].removeFirst();
if (m_oldPath[fileIndex].isEmpty())
m_oldPath.remove(fileIndex);
#endif
--m_renameCount;
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
@@ -1916,16 +1919,19 @@ void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert
void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
{
const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index);
Q_ASSERT(fileIndex >= 0);
qDebug("A file completed download in torrent \"%s\"", qUtf8Printable(name()));
if (m_session->isAppendExtensionEnabled())
{
QString name = filePath(static_cast<LTUnderlyingType<lt::file_index_t>>(p->index));
QString name = filePath(fileIndex);
if (name.endsWith(QB_EXT))
{
const QString oldName = name;
name.chop(QB_EXT.size());
qDebug("Renaming %s to %s", qUtf8Printable(oldName), qUtf8Printable(name));
renameFile(static_cast<LTUnderlyingType<lt::file_index_t>>(p->index), name);
renameFile(fileIndex, name);
}
}
}
@@ -1935,7 +1941,7 @@ void TorrentImpl::handleFileErrorAlert(const lt::file_error_alert *p)
m_lastFileError = {p->error, p->op};
}
#if (LIBTORRENT_VERSION_NUM >= 20003)
#ifdef QBT_USES_LIBTORRENT2
void TorrentImpl::handleFilePrioAlert(const lt::file_prio_alert *)
{
if (m_nativeHandle.need_save_resume_data())
@@ -1980,7 +1986,7 @@ void TorrentImpl::handleAlert(const lt::alert *a)
{
switch (a->type())
{
#if (LIBTORRENT_VERSION_NUM >= 20003)
#ifdef QBT_USES_LIBTORRENT2
case lt::file_prio_alert::alert_type:
handleFilePrioAlert(static_cast<const lt::file_prio_alert*>(a));
break;
@@ -2258,7 +2264,8 @@ QString TorrentImpl::createMagnetURI() const
void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
{
if (!hasMetadata()) return;
if (priorities.size() != filesCount()) return;
Q_ASSERT(priorities.size() == filesCount());
// Reset 'm_hasSeedStatus' if needed in order to react again to
// 'torrent_finished_alert' and eg show tray notifications
@@ -2275,8 +2282,14 @@ void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
}
}
const int internalFilesCount = m_torrentInfo.nativeInfo()->files().num_files(); // including .pad files
auto nativePriorities = std::vector<lt::download_priority_t>(internalFilesCount, LT::toNative(DownloadPriority::Normal));
const auto nativeIndexes = m_torrentInfo.nativeIndexes();
for (int i = 0; i < priorities.size(); ++i)
nativePriorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(priorities[i]);
qDebug() << Q_FUNC_INFO << "Changing files priorities...";
m_nativeHandle.prioritize_files(toLTDownloadPriorities(priorities));
m_nativeHandle.prioritize_files(nativePriorities);
// Restore first/last piece first option if necessary
if (m_hasFirstLastPiecePriority)

View File

@@ -51,16 +51,6 @@
#include "torrent.h"
#include "torrentinfo.h"
#if (LIBTORRENT_VERSION_NUM == 20003)
// file_prio_alert is missing to be forward declared in "libtorrent/fwd.hpp"
namespace libtorrent
{
TORRENT_VERSION_NAMESPACE_3
struct file_prio_alert;
TORRENT_VERSION_NAMESPACE_3_END
}
#endif
namespace BitTorrent
{
class Session;
@@ -86,7 +76,7 @@ namespace BitTorrent
class TorrentImpl final : public QObject, public Torrent
{
Q_DISABLE_COPY(TorrentImpl)
Q_DISABLE_COPY_MOVE(TorrentImpl)
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentImpl)
public:
@@ -136,8 +126,8 @@ namespace BitTorrent
int seedingTimeLimit() const override;
QString filePath(int index) const override;
QString fileName(int index) const override;
qlonglong fileSize(int index) const override;
QStringList filePaths() const override;
QStringList absoluteFilePaths() const override;
QVector<DownloadPriority> filePriorities() const override;
@@ -159,7 +149,6 @@ namespace BitTorrent
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;
QVector<QUrl> urlSeeds() const override;
@@ -262,7 +251,7 @@ namespace BitTorrent
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p);
void handleFileCompletedAlert(const lt::file_completed_alert *p);
void handleFileErrorAlert(const lt::file_error_alert *p);
#if (LIBTORRENT_VERSION_NUM >= 20003)
#ifdef QBT_USES_LIBTORRENT2
void handleFilePrioAlert(const lt::file_prio_alert *p);
#endif
void handleFileRenamedAlert(const lt::file_renamed_alert *p);
@@ -312,9 +301,11 @@ namespace BitTorrent
MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
// Until libtorrent provide an "old_name" field in `file_renamed_alert`
// we will rely on this workaround to remove empty leftover folders
QHash<lt::file_index_t, QVector<QString>> m_oldPath;
#ifndef QBT_USES_LIBTORRENT2
// Until libtorrent provided an "old_name" field in `file_renamed_alert`
// we relied on this workaround to remove empty leftover folders
QHash<int, QVector<QString>> m_oldPath;
#endif
QHash<QString, QMap<lt::tcp::endpoint, int>> m_trackerPeerCounts;
FileErrorInfo m_lastFileError;

View File

@@ -28,10 +28,8 @@
#include "torrentinfo.h"
#include <libtorrent/bencode.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/error_code.hpp>
#include <libtorrent/version.hpp>
#include <QByteArray>
#include <QDateTime>
@@ -42,7 +40,6 @@
#include <QUrl>
#include <QVector>
#include "base/exceptions.h"
#include "base/global.h"
#include "base/utils/fs.h"
#include "base/utils/io.h"
@@ -61,7 +58,7 @@ namespace
{
if (QDir::isAbsolutePath(filePath)) continue;
const auto filePathElements = filePath.splitRef('/');
const auto filePathElements = QStringView(filePath).split(u'/');
// if at least one file has no root folder, no common root folder exists
if (filePathElements.count() <= 1) return {};
@@ -78,22 +75,37 @@ namespace
const int torrentInfoId = qRegisterMetaType<TorrentInfo>();
TorrentInfo::TorrentInfo(std::shared_ptr<const lt::torrent_info> nativeInfo)
: m_nativeInfo {std::const_pointer_cast<lt::torrent_info>(nativeInfo)}
{
m_nativeInfo = std::const_pointer_cast<lt::torrent_info>(nativeInfo);
if (!m_nativeInfo)
return;
const lt::file_storage &fileStorage = m_nativeInfo->files();
m_nativeIndexes.reserve(fileStorage.num_files());
for (const lt::file_index_t nativeIndex : fileStorage.file_range())
{
if (!fileStorage.pad_file_at(nativeIndex))
m_nativeIndexes.append(nativeIndex);
}
}
TorrentInfo::TorrentInfo(const TorrentInfo &other)
: m_nativeInfo(other.m_nativeInfo)
: m_nativeInfo {other.m_nativeInfo}
, m_nativeIndexes {other.m_nativeIndexes}
{
}
TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other)
{
m_nativeInfo = other.m_nativeInfo;
if (this != &other)
{
m_nativeInfo = other.m_nativeInfo;
m_nativeIndexes = other.m_nativeIndexes;
}
return *this;
}
TorrentInfo TorrentInfo::load(const QByteArray &data, QString *error) noexcept
nonstd::expected<TorrentInfo, QString> TorrentInfo::load(const QByteArray &data) noexcept
{
// 2-step construction to overcome default limits of `depth_limit` & `token_limit` which are
// used in `torrent_info()` constructor
@@ -104,42 +116,23 @@ TorrentInfo TorrentInfo::load(const QByteArray &data, QString *error) noexcept
const lt::bdecode_node node = lt::bdecode(data, ec
, nullptr, depthLimit, tokenLimit);
if (ec)
{
if (error)
*error = QString::fromStdString(ec.message());
return TorrentInfo();
}
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
TorrentInfo info {std::shared_ptr<lt::torrent_info>(new lt::torrent_info(node, ec))};
if (ec)
{
if (error)
*error = QString::fromStdString(ec.message());
return TorrentInfo();
}
return nonstd::make_unexpected(QString::fromStdString(ec.message()));
return info;
}
TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexcept
nonstd::expected<TorrentInfo, QString> TorrentInfo::loadFromFile(const QString &path) noexcept
{
if (error)
error->clear();
QFile file {path};
if (!file.open(QIODevice::ReadOnly))
{
if (error)
*error = file.errorString();
return TorrentInfo();
}
return nonstd::make_unexpected(file.errorString());
if (file.size() > MAX_TORRENT_SIZE)
{
if (error)
*error = tr("File size exceeds max limit %1").arg(Utils::Misc::friendlyUnit(MAX_TORRENT_SIZE));
return TorrentInfo();
}
return nonstd::make_unexpected(tr("File size exceeds max limit %1").arg(Utils::Misc::friendlyUnit(MAX_TORRENT_SIZE)));
QByteArray data;
try
@@ -148,44 +141,36 @@ TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexc
}
catch (const std::bad_alloc &e)
{
if (error)
*error = tr("Torrent file read error: %1").arg(e.what());
return TorrentInfo();
return nonstd::make_unexpected(tr("Torrent file read error: %1").arg(e.what()));
}
if (data.size() != file.size())
{
if (error)
*error = tr("Torrent file read error: size mismatch");
return TorrentInfo();
}
return nonstd::make_unexpected(tr("Torrent file read error: size mismatch"));
file.close();
return load(data, error);
return load(data);
}
void TorrentInfo::saveToFile(const QString &path) const
nonstd::expected<void, QString> TorrentInfo::saveToFile(const QString &path) const
{
if (!isValid())
throw RuntimeError {tr("Invalid metadata")};
return nonstd::make_unexpected(tr("Invalid metadata"));
try
{
const auto torrentCreator = lt::create_torrent(*nativeInfo());
const lt::entry torrentEntry = torrentCreator.generate();
QFile torrentFile {path};
if (!torrentFile.open(QIODevice::WriteOnly))
throw RuntimeError(torrentFile.errorString());
lt::bencode(Utils::IO::FileDeviceOutputIterator {torrentFile}, torrentEntry);
if (torrentFile.error() != QFileDevice::NoError)
throw RuntimeError(torrentFile.errorString());
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, torrentEntry);
if (!result)
return result.get_unexpected();
}
catch (const lt::system_error &err)
{
throw RuntimeError(QString::fromLocal8Bit(err.what()));
return nonstd::make_unexpected(QString::fromLocal8Bit(err.what()));
}
return {};
}
bool TorrentInfo::isValid() const
@@ -197,7 +182,7 @@ InfoHash TorrentInfo::infoHash() const
{
if (!isValid()) return {};
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
return m_nativeInfo->info_hashes();
#else
return m_nativeInfo->info_hash();
@@ -245,7 +230,7 @@ qlonglong TorrentInfo::totalSize() const
int TorrentInfo::filesCount() const
{
if (!isValid()) return -1;
return m_nativeInfo->num_files();
return m_nativeIndexes.size();
}
int TorrentInfo::pieceLength() const
@@ -270,40 +255,36 @@ QString TorrentInfo::filePath(const int index) const
{
if (!isValid()) return {};
return Utils::Fs::toUniformPath(
QString::fromStdString(m_nativeInfo->files().file_path(lt::file_index_t {index})));
QString::fromStdString(m_nativeInfo->files().file_path(m_nativeIndexes[index])));
}
QStringList TorrentInfo::filePaths() const
{
QStringList list;
list.reserve(filesCount());
for (int i = 0; i < filesCount(); ++i)
list << filePath(i);
return list;
}
QString TorrentInfo::fileName(const int index) const
{
return Utils::Fs::fileName(filePath(index));
}
QString TorrentInfo::origFilePath(const int index) const
{
if (!isValid()) return {};
return Utils::Fs::toUniformPath(
QString::fromStdString(m_nativeInfo->orig_files().file_path(lt::file_index_t {index})));
QString::fromStdString(m_nativeInfo->orig_files().file_path(m_nativeIndexes[index])));
}
qlonglong TorrentInfo::fileSize(const int index) const
{
if (!isValid()) return -1;
return m_nativeInfo->files().file_size(lt::file_index_t {index});
return m_nativeInfo->files().file_size(m_nativeIndexes[index]);
}
qlonglong TorrentInfo::fileOffset(const int index) const
{
if (!isValid()) return -1;
return m_nativeInfo->files().file_offset(lt::file_index_t {index});
return m_nativeInfo->files().file_offset(m_nativeIndexes[index]);
}
QVector<TrackerEntry> TorrentInfo::trackers() const
@@ -342,7 +323,7 @@ QVector<QUrl> TorrentInfo::urlSeeds() const
QByteArray TorrentInfo::metadata() const
{
if (!isValid()) return {};
#if (LIBTORRENT_VERSION_NUM >= 20000)
#ifdef QBT_USES_LIBTORRENT2
const lt::span<const char> infoSection {m_nativeInfo->info_section()};
return {infoSection.data(), static_cast<int>(infoSection.size())};
#else
@@ -372,8 +353,12 @@ QVector<int> TorrentInfo::fileIndicesForPiece(const int pieceIndex) const
lt::piece_index_t {pieceIndex}, 0, nativeInfo()->piece_size(lt::piece_index_t {pieceIndex}));
QVector<int> res;
res.reserve(static_cast<decltype(res)::size_type>(files.size()));
std::transform(files.begin(), files.end(), std::back_inserter(res),
[](const lt::file_slice &s) { return static_cast<int>(s.file_index); });
for (const lt::file_slice &fileSlice : files)
{
const int index = m_nativeIndexes.indexOf(fileSlice.file_index);
if (index >= 0)
res.append(index);
}
return res;
}
@@ -419,8 +404,8 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const
}
const lt::file_storage &files = nativeInfo()->files();
const auto fileSize = files.file_size(lt::file_index_t {fileIndex});
const auto fileOffset = files.file_offset(lt::file_index_t {fileIndex});
const auto fileSize = files.file_size(m_nativeIndexes[fileIndex]);
const auto fileOffset = files.file_offset(m_nativeIndexes[fileIndex]);
const int beginIdx = (fileOffset / pieceLength());
const int endIdx = ((fileOffset + fileSize - 1) / pieceLength());
@@ -433,7 +418,7 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const
void TorrentInfo::renameFile(const int index, const QString &newPath)
{
if (!isValid()) return;
nativeInfo()->rename_file(lt::file_index_t {index}, Utils::Fs::toNativePath(newPath).toStdString());
nativeInfo()->rename_file(m_nativeIndexes[index], Utils::Fs::toNativePath(newPath).toStdString());
}
int TorrentInfo::fileIndex(const QString &fileName) const
@@ -485,8 +470,8 @@ void TorrentInfo::stripRootFolder()
if (files.name() != newName)
{
files.set_name(newName);
for (int i = 0; i < files.num_files(); ++i)
files.rename_file(lt::file_index_t {i}, files.file_path(lt::file_index_t {i}));
for (const lt::file_index_t nativeIndex : files.file_range())
files.rename_file(nativeIndex, files.file_path(nativeIndex));
}
files.set_name("");
@@ -505,8 +490,8 @@ void TorrentInfo::addRootFolder()
const std::string rootPrefix = Utils::Fs::toNativePath(rootFolder + QLatin1Char {'/'}).toStdString();
lt::file_storage files = m_nativeInfo->files();
files.set_name(rootFolder.toStdString());
for (int i = 0; i < files.num_files(); ++i)
files.rename_file(lt::file_index_t {i}, rootPrefix + files.file_path(lt::file_index_t {i}));
for (const lt::file_index_t nativeIndex : files.file_range())
files.rename_file(nativeIndex, rootPrefix + files.file_path(nativeIndex));
m_nativeInfo->remap_files(files);
}
@@ -527,3 +512,8 @@ std::shared_ptr<lt::torrent_info> TorrentInfo::nativeInfo() const
{
return m_nativeInfo;
}
QVector<lt::file_index_t> TorrentInfo::nativeIndexes() const
{
return m_nativeIndexes;
}

View File

@@ -33,6 +33,7 @@
#include <QCoreApplication>
#include <QtContainerFwd>
#include "base/3rdparty/expected.hpp"
#include "base/indexrange.h"
#include "abstractfilestorage.h"
#include "torrentcontentlayout.h"
@@ -55,9 +56,9 @@ namespace BitTorrent
explicit TorrentInfo(std::shared_ptr<const lt::torrent_info> nativeInfo = {});
TorrentInfo(const TorrentInfo &other);
static TorrentInfo load(const QByteArray &data, QString *error = nullptr) noexcept;
static TorrentInfo loadFromFile(const QString &path, QString *error = nullptr) noexcept;
void saveToFile(const QString &path) const;
static nonstd::expected<TorrentInfo, QString> load(const QByteArray &data) noexcept;
static nonstd::expected<TorrentInfo, QString> loadFromFile(const QString &path) noexcept;
nonstd::expected<void, QString> saveToFile(const QString &path) const;
TorrentInfo &operator=(const TorrentInfo &other);
@@ -75,7 +76,6 @@ namespace BitTorrent
int piecesCount() const;
QString filePath(int index) const override;
QStringList filePaths() const;
QString fileName(int index) const override;
QString origFilePath(int index) const;
qlonglong fileSize(int index) const override;
qlonglong fileOffset(int index) const;
@@ -99,6 +99,7 @@ namespace BitTorrent
void setContentLayout(TorrentContentLayout layout);
std::shared_ptr<lt::torrent_info> nativeInfo() const;
QVector<lt::file_index_t> nativeIndexes() const;
private:
// returns file index or -1 if fileName is not found
@@ -108,6 +109,10 @@ namespace BitTorrent
TorrentContentLayout defaultContentLayout() const;
std::shared_ptr<lt::torrent_info> m_nativeInfo;
// internal indexes of files (payload only, excluding any .pad files)
// by which they are addressed in libtorrent
QVector<lt::file_index_t> m_nativeIndexes;
};
}

View File

@@ -72,7 +72,7 @@ namespace BitTorrent
class Tracker final : public QObject, public Http::IRequestHandler, private Http::ResponseBuilder
{
Q_OBJECT
Q_DISABLE_COPY(Tracker)
Q_DISABLE_COPY_MOVE(Tracker)
struct TrackerAnnounceRequest;

View File

@@ -137,9 +137,9 @@ bool Connection::acceptsGzipEncoding(QString codings)
{
// [rfc7231] 5.3.4. Accept-Encoding
const auto isCodingAvailable = [](const QVector<QStringRef> &list, const QString &encoding) -> bool
const auto isCodingAvailable = [](const QList<QStringView> &list, const QStringView encoding) -> bool
{
for (const QStringRef &str : list)
for (const QStringView &str : list)
{
if (!str.startsWith(encoding))
continue;
@@ -149,7 +149,7 @@ bool Connection::acceptsGzipEncoding(QString codings)
return true;
// [rfc7231] 5.3.1. Quality Values
const QStringRef substr = str.mid(encoding.size() + 3); // ex. skip over "gzip;q="
const QStringView substr = str.mid(encoding.size() + 3); // ex. skip over "gzip;q="
bool ok = false;
const double qvalue = substr.toDouble(&ok);
@@ -161,15 +161,15 @@ bool Connection::acceptsGzipEncoding(QString codings)
return false;
};
const QVector<QStringRef> list = codings.remove(' ').remove('\t').splitRef(',', Qt::SkipEmptyParts);
const QList<QStringView> list = QStringView(codings.remove(' ').remove('\t')).split(u',', Qt::SkipEmptyParts);
if (list.isEmpty())
return false;
const bool canGzip = isCodingAvailable(list, QLatin1String("gzip"));
const bool canGzip = isCodingAvailable(list, QString::fromLatin1("gzip"));
if (canGzip)
return true;
const bool canAny = isCodingAvailable(list, QLatin1String("*"));
const bool canAny = isCodingAvailable(list, QString::fromLatin1("*"));
if (canAny)
return true;

View File

@@ -43,7 +43,7 @@ namespace Http
class Connection : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Connection)
Q_DISABLE_COPY_MOVE(Connection)
public:
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = nullptr);

View File

@@ -57,7 +57,7 @@ namespace
return in;
}
bool parseHeaderLine(const QString &line, HeaderMap &out)
bool parseHeaderLine(const QStringView line, HeaderMap &out)
{
// [rfc7230] 3.2. Header Fields
const int i = line.indexOf(':');
@@ -67,8 +67,8 @@ namespace
return false;
}
const QString name = line.leftRef(i).trimmed().toString().toLower();
const QString value = line.midRef(i + 1).trimmed().toString();
const QString name = line.left(i).trimmed().toString().toLower();
const QString value = line.mid(i + 1).trimmed().toString();
out[name] = value;
return true;
@@ -145,10 +145,10 @@ RequestParser::ParseResult RequestParser::doParse(const QByteArray &data)
return {ParseStatus::BadRequest, Request(), 0}; // TODO: SHOULD respond "501 Not Implemented"
}
bool RequestParser::parseStartLines(const QString &data)
bool RequestParser::parseStartLines(const QStringView data)
{
// we don't handle malformed request which uses `LF` for newline
const QVector<QStringRef> lines = data.splitRef(CRLF, Qt::SkipEmptyParts);
const QList<QStringView> lines = data.split(QString::fromLatin1(CRLF), Qt::SkipEmptyParts);
// [rfc7230] 3.2.2. Field Order
QStringList requestLines;
@@ -267,7 +267,7 @@ bool RequestParser::parsePostMessage(const QByteArray &data)
return false;
}
const QByteArray delimiter = Utils::String::unquote(contentType.midRef(idx + boundaryFieldName.size())).toLatin1();
const QByteArray delimiter = Utils::String::unquote(QStringView(contentType).mid(idx + boundaryFieldName.size())).toLatin1();
if (delimiter.isEmpty())
{
qWarning() << Q_FUNC_INFO << "boundary delimiter field empty!";
@@ -311,13 +311,13 @@ bool RequestParser::parseFormData(const QByteArray &data)
const QByteArray payload = viewWithoutEndingWith(list[1], CRLF);
HeaderMap headersMap;
const QVector<QStringRef> headerLines = headers.splitRef(CRLF, Qt::SkipEmptyParts);
const QList<QStringView> headerLines = QStringView(headers).split(QString::fromLatin1(CRLF), Qt::SkipEmptyParts);
for (const auto &line : headerLines)
{
if (line.trimmed().startsWith(HEADER_CONTENT_DISPOSITION, Qt::CaseInsensitive))
if (line.trimmed().startsWith(QString::fromLatin1(HEADER_CONTENT_DISPOSITION), Qt::CaseInsensitive))
{
// extract out filename & name
const QVector<QStringRef> directives = line.split(';', Qt::SkipEmptyParts);
const QList<QStringView> directives = line.split(u';', Qt::SkipEmptyParts);
for (const auto &directive : directives)
{

View File

@@ -60,7 +60,7 @@ namespace Http
RequestParser();
ParseResult doParse(const QByteArray &data);
bool parseStartLines(const QString &data);
bool parseStartLines(QStringView data);
bool parseRequestLine(const QString &line);
bool parsePostMessage(const QByteArray &data);

View File

@@ -43,7 +43,7 @@ namespace Http
class Server final : public QTcpServer
{
Q_OBJECT
Q_DISABLE_COPY(Server)
Q_DISABLE_COPY_MOVE(Server)
public:
explicit Server(IRequestHandler *requestHandler, QObject *parent = nullptr);

View File

@@ -35,7 +35,7 @@ class QString;
class IconProvider : public QObject
{
Q_DISABLE_COPY(IconProvider)
Q_DISABLE_COPY_MOVE(IconProvider)
public:
static void initInstance();

View File

@@ -72,7 +72,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(Log::MsgTypes)
class Logger : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Logger)
Q_DISABLE_COPY_MOVE(Logger)
public:
static void initInstance();

View File

@@ -213,7 +213,7 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
if (code == "badagent")
{
logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."),
logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please submit a bug report at http://bugs.qbittorrent.org."),
Log::CRITICAL);
m_state = FATAL;
return;
@@ -221,7 +221,7 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
if (code == "!donator")
{
logger->addMessage(tr("Dynamic DNS error: %1 was returned by the service, please report a bug at http://bugs.qbittorrent.org.").arg("!donator"),
logger->addMessage(tr("Dynamic DNS error: %1 was returned by the service, please submit a bug report at http://bugs.qbittorrent.org.").arg("!donator"),
Log::CRITICAL);
m_state = FATAL;
return;

View File

@@ -32,26 +32,24 @@
#include <QTemporaryFile>
#include <QUrl>
#include "base/3rdparty/expected.hpp"
#include "base/utils/fs.h"
#include "base/utils/gzip.h"
#include "base/utils/io.h"
#include "base/utils/misc.h"
const int MAX_REDIRECTIONS = 20; // the common value for web browsers
namespace
{
bool saveToFile(const QByteArray &replyData, QString &filePath)
nonstd::expected<QString, QString> saveToTempFile(const QByteArray &data)
{
QTemporaryFile tmpfile {Utils::Fs::tempPath() + "XXXXXX"};
tmpfile.setAutoRemove(false);
QTemporaryFile file {Utils::Fs::tempPath()};
if (!file.open() || (file.write(data) != data.length()) || !file.flush())
return nonstd::make_unexpected(file.errorString());
if (!tmpfile.open())
return false;
filePath = tmpfile.fileName();
tmpfile.write(replyData);
return true;
file.setAutoRemove(false);
return file.fileName();
}
}
@@ -129,11 +127,23 @@ void DownloadHandlerImpl::processFinishedDownload()
if (m_downloadRequest.saveToFile())
{
QString filePath;
if (saveToFile(m_result.data, filePath))
m_result.filePath = filePath;
const QString destinationPath = m_downloadRequest.destFileName();
if (destinationPath.isEmpty())
{
const nonstd::expected<QString, QString> result = saveToTempFile(m_result.data);
if (result)
m_result.filePath = result.value();
else
setError(tr("I/O Error: %1").arg(result.error()));
}
else
setError(tr("I/O Error"));
{
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(destinationPath, m_result.data);
if (result)
m_result.filePath = destinationPath;
else
setError(tr("I/O Error: %1").arg(result.error()));
}
}
finish();

View File

@@ -39,7 +39,7 @@ class QUrl;
class DownloadHandlerImpl final : public Net::DownloadHandler
{
Q_OBJECT
Q_DISABLE_COPY(DownloadHandlerImpl)
Q_DISABLE_COPY_MOVE(DownloadHandlerImpl)
public:
DownloadHandlerImpl(Net::DownloadManager *manager, const Net::DownloadRequest &downloadRequest);

View File

@@ -348,6 +348,17 @@ Net::DownloadRequest &Net::DownloadRequest::saveToFile(const bool value)
return *this;
}
QString Net::DownloadRequest::destFileName() const
{
return m_destFileName;
}
Net::DownloadRequest &Net::DownloadRequest::destFileName(const QString &value)
{
m_destFileName = value;
return *this;
}
Net::ServiceID Net::ServiceID::fromURL(const QUrl &url)
{
return {url.host(), url.port(80)};

View File

@@ -78,11 +78,18 @@ namespace Net
bool saveToFile() const;
DownloadRequest &saveToFile(bool value);
// if saveToFile is set, the file is saved in destFileName
// (deprecated) if destFileName is not provided, the file will be saved
// in a temporary file, the name of file is set in DownloadResult::filePath
QString destFileName() const;
DownloadRequest &destFileName(const QString &value);
private:
QString m_url;
QString m_userAgent;
qint64 m_limit = 0;
bool m_saveToFile = false;
QString m_destFileName;
};
struct DownloadResult
@@ -98,7 +105,7 @@ namespace Net
class DownloadHandler : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(DownloadHandler)
Q_DISABLE_COPY_MOVE(DownloadHandler)
public:
using QObject::QObject;
@@ -112,7 +119,7 @@ namespace Net
class DownloadManager : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(DownloadManager)
Q_DISABLE_COPY_MOVE(DownloadManager)
public:
static void initInstance();

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