Compare commits

..

303 Commits

Author SHA1 Message Date
sledgehammer999
063844ed44 Bump to 4.3.5 2021-05-02 18:59:11 +03:00
sledgehammer999
899f8a3f61 Update Changelog 2021-05-02 18:59:10 +03:00
sledgehammer999
0f72f22096 Sync translations from Transifex and run lupdate 2021-05-02 18:33:49 +03:00
Felipe
ffda0ab0ac Update portugueseBR.nsi
An updated/fixed version of the installer language file.
2021-05-02 17:41:08 +03:00
Долматов Алексей
21f18623db Update russian.nsi
Update uninstall strings.
2021-05-02 17:41:06 +03:00
PriitUring
c4265db0fd Update estonian.nsi
Changing translation of word "Uninstall" to more common local word "Desinstall".
2021-05-02 17:41:04 +03:00
Henry Water
bc8d9656f6 Update german.nsi
Line 32
Changing "zumindest" to "mindestens".
I even would go further to change the line into "[...] Windows 7 oder höher." ("[...] Windows 7 or higher."), as this seems to make more sense and we wouldn't have the hassle of discussing whether "zumindest" or "mindestens" fits better.

If you insist on a comment:
It is quite a cosmetic change, and a personal opinion as well. Imo "mindestens" fits more to the theme of general use - "zumindest" can be used in special places, as it isn't that common in e.g. regular conversations. What comes to my mind is "Best before", which is "Mindestens haltbar bis". It would be unusual to use "Zumindest haltbar bis". 😄

Finally it is up to you what to change.

CC: @schnurlos

Additionally I fixed line 10, where no need of a double-"mit" was.
Plus line 34, as requested!
2021-05-02 17:41:02 +03:00
Chocobo1
bd284facbd Add new translatable string to all available languages
Also provide translation for Traditional Chinese.
Fix up a2ef09466f.
2021-05-02 17:40:59 +03:00
bovirus
7eb77cbcbd Enable translation for uninstaller link (#14660) 2021-05-02 17:40:57 +03:00
sledgehammer999
86e96b819e Clarify that the license is GPLv2+
Regarding this license clarification there are 3 commits of interest
(commits A, B, C). Before commit A the COPYING file contained only the
text of the GPLv2 license, while all source files had a license block
at the top saying that they are under the terms of "GPLv2 or later". With
commit A there was a temporary change to GPLv3. The COPYING file contained
only the text of the GPLv3 license, while all source files had a license
block at the top saying that they are under the terms of "GPLv3 or later".
Then with commit B the COPYING file and the license block of the source
files was reverted to their state before commit A. Afterwards, with
commit C a license summary(or clarification) block was put at the top of
the COPYING file. This block indicated that the license was GPLv2 without
having the "or later" clause and it also included the OpenSSL exception.
However, the license block of each source file continued to contain the
"or later" clause which was not removed. The same license block continues
to exist in all current source files. Thus it is concluded that the ommision
of the "or later" clause with commit C in the COPYING file was accidental.
OR ALTERNATIVELY (OR IN ADDITION)
At the time commit C was made Christophe Dumez was not the sole contributor.
There is no record that the other contributors agreed with the supposed
GPLv2 only change or that there was a Contributor License Agreement,
transfering their rights to him. Thus making his license change decision
invalid/void/illegal.

Commit A: 54f9375b32
Commit B: 8df61db644
Commit C: 9835af4962
2021-05-02 17:40:56 +03:00
Chocobo1
132be7ee9d Move cursor to the next line of end of text
Closes #13908.
2021-05-02 17:40:10 +03:00
Chocobo1
9f8a6e8fb1 Merge pull request #14872 from jagannatharjun/pfix-back
Correctly draw progress background with stylesheet (backport v4_3_x)
2021-04-30 10:09:31 +08:00
jagannatharjun
e6447c8f28 Correctly draw progress background with stylesheet
fixes #14731
2021-04-29 11:28:20 +05:30
Vladimir Golovnev
7dd9e73431 Merge pull request #14865 from Kolcha/cmake_mac_4_3
Improve Info.plist generation with CMake (v4_3_x backport)
2021-04-28 14:18:39 +03:00
Nick Korotysh
ea82962c5d Improve Info.plist generation with CMake
- drop configure_file() and file(GENERATE) calls
- fill missed MACOSX_DEPLOYMENT_TARGET variable
2021-04-27 23:51:13 +03:00
Vladimir Golovnev
b2a43eeffc Merge pull request #14828 from Kolcha/bundle_name_4_3
Don't use executable name as CFBundleName value (v4_3_x backport)
2021-04-25 07:02:41 +03:00
Vladimir Golovnev
681347148d Merge pull request #14832 from glassez/v4.3-upload-mode
Correctly handle "not enough disk space" error (v4_3_x backport)
2021-04-25 07:01:27 +03:00
Vladimir Golovnev (Glassez)
1f63490755 Correctly handle "no enough disk space" error
If torrent failed to write, it stops downloading and goes to
"upload mode" instead of errored state so it just keeps seeding.
Now qBittorrent indicates this state as "errored" and allows
the user to manually bring the torrent out of this state.
2021-04-22 18:53:20 +03:00
Nick Korotysh
0140ed356f Don't use executable name as CFBundleName value 2021-04-21 21:07:25 +03:00
Chocobo1
2c8f322af5 Merge pull request #14801 from Chocobo1/backport
Backport PRs to v4_3_x
2021-04-18 12:42:15 +08:00
Vladimir Golovnev (Glassez)
6f31ebd899 Allow to specify file indexes in torrents/files API 2021-04-17 11:46:41 +08:00
zhuangzi926
014df529c5 Update dyndns register url 2021-04-17 11:46:20 +08:00
Chocobo1
482dad00fd Fix D-Bus Notification desktop-entry field 2021-04-16 14:22:32 +08:00
Chocobo1
7898037006 Merge pull request #14701 from Chocobo1/backport
Backport to v4_3_x
2021-04-09 11:32:36 +08:00
skvenders
5c819ee384 Remove contributor
Remove contributor as requested: https://github.com/qbittorrent/qBittorrent/pull/14637#issuecomment-808894421
2021-04-08 12:06:35 +08:00
brvphoenix
a590e7139b WebUI: Fix magnet url from the browser 2021-04-08 12:05:56 +08:00
Chocobo1
89a8e07217 Revise folder monitoring functions in WebUI
Closes #14241.
2021-04-03 14:52:14 +08:00
Chocobo1
5c05bdaa27 Clean up code 2021-04-03 14:52:14 +08:00
Chocobo1
7006afc611 Remove unnecessary URL encoding
Fix #14635.
2021-04-03 14:51:24 +08:00
Christoph Rackwitz
9cb3a6d29e Fix tabChangesFocus attribute in "Edit trackers" dialog 2021-04-03 14:49:03 +08:00
Chocobo1
2b6baa6099 Merge pull request #14685 from Chocobo1/actions
Enable Github Actions CI for other branches
2021-04-03 13:21:36 +08:00
Chocobo1
8881035b7a Enable Github Actions CI for other branches
Since we need to backport some changes.
2021-04-01 12:50:00 +08:00
sledgehammer999
2be30a50ef Merge pull request #14628 from sledgehammer999/qt_511
Lower Qt requirement to 5.11
2021-03-27 17:56:43 +02:00
Mike Tzou
81a7b0c034 Merge pull request #14625 from Chocobo1/backport
Backport to v4_3_x (#14619, #14590)
2021-03-27 11:14:26 +08:00
sledgehammer999
e7235cc3f8 Revert "Use QRegularExpression instead of deprecated QRegExp"
Related to #14611

This reverts commit 3b748178c2.
2021-03-26 12:43:51 +02:00
sledgehammer999
1570b51f6c Lower Qt requirement to 5.11
Partially reverts 4037143f4e
Closes #14611
2021-03-26 12:42:08 +02:00
Chocobo1
6272c6d95d Apply code formatting 2021-03-26 10:48:34 +08:00
Chocobo1
473ae25fd8 Add WebUI checking to CI 2021-03-26 10:48:34 +08:00
Chocobo1
42d7d9b5f4 Add necessary curly brackets 2021-03-26 10:48:33 +08:00
Chocobo1
0f77b00428 Don't use Object.prototypes builtins directly
See: https://eslint.org/docs/rules/no-prototype-builtins
2021-03-26 10:48:33 +08:00
Chocobo1
060804d3b8 Remove unnecessary escape character 2021-03-26 10:48:33 +08:00
Chocobo1
f8b6cb4879 Remove extra semicolon 2021-03-26 10:48:13 +08:00
Chocobo1
a6d27223db Add tools for js code formatting and linting
Due to eslint couldn't correctly resolve the context for the variables,
two eslint rules are disabled for now.
2021-03-26 10:48:13 +08:00
Chocobo1
bb32b88a62 Sort invalid QDateTime values after valid values
Closes #14607.
2021-03-26 10:47:54 +08:00
sledgehammer999
332b173e08 Bump to 4.3.4.1 2021-03-24 21:21:06 +02:00
sledgehammer999
e921cf677a Update Changelog 2021-03-24 21:19:55 +02:00
Vladimir Golovnev (Glassez)
973b5a4809 Correctly draw progress bar in Qt 6 2021-03-24 21:15:49 +02:00
Chocobo1
688e11a911 Remove wrong parentheses
Fix up 87ad8a1495.
2021-03-24 19:17:30 +02:00
sledgehammer999
f7e6b96493 Bump to 4.3.4 2021-03-23 23:14:15 +02:00
sledgehammer999
88bf6f11c7 Update Changelog 2021-03-23 23:04:12 +02:00
sledgehammer999
90e2236990 Sync translations from Transifex and run lupdate 2021-03-23 23:02:22 +02:00
treysis
6ad7cadc4b Fix bad IPv6 address format for outgoingInterfaces
Fixes https://github.com/qbittorrent/qBittorrent/issues/12892#issuecomment-792292336
2021-03-23 22:26:59 +02:00
brvphoenix
0499111156 WebUI: Avoid decoding strings repeatedly
Fix #14553
2021-03-23 22:26:57 +02:00
Vladimir Golovnev (Glassez)
ae44e59c9a Wrap "resume data" in LoadTorrentParams 2021-03-23 22:26:55 +02:00
Vladimir Golovnev (Glassez)
1de52f9bcf Drop deprecated code 2021-03-23 22:26:54 +02:00
Vladimir Golovnev (Glassez)
448e55031e Save resume data when torrent has done checking 2021-03-23 22:26:52 +02:00
Vladimir Golovnev (Glassez)
3b748178c2 Use QRegularExpression instead of deprecated QRegExp
Now it follows closely the definition of wildcard for glob patterns.
The backslash (\) character is not an escape char in this context.
In order to match one of the special characters, place it in square
brackets (for example, [?]).
2021-03-23 22:26:50 +02:00
thalieht
a4a54ce712 Allow >100 days in WebUI function "friendlyDuration"
Because it's not only used for ETA.
2021-03-23 22:26:48 +02:00
thalieht
d19b524d2d Fix incorrect seeding time string in WebUI General tab 2021-03-23 22:26:47 +02:00
thalieht
1e2bf50e66 Add seeding time to the active time column in WebUI
Closes #14526
2021-03-23 22:26:46 +02:00
Vladimir Golovnev (Glassez)
e7f3409053 Don't use deprecated operators 2021-03-23 22:26:44 +02:00
Vladimir Golovnev (Glassez)
9758633eeb Use correct return statement 2021-03-23 22:26:42 +02:00
Vladimir Golovnev (Glassez)
3def5e40c4 Include missing header 2021-03-23 22:26:41 +02:00
Vladimir Golovnev (Glassez)
ca923ed02c Include QDesktopWidget header only when needed 2021-03-23 22:26:38 +02:00
Chocobo1
e4c3bad93a Fix library requirements 2021-03-23 22:26:37 +02:00
Chocobo1
3b52c5ce97 Draw progress bar in disabled style 2021-03-23 22:26:36 +02:00
Vladimir Golovnev (Glassez)
44b94803a4 Improve "save resume data" handling 2021-03-23 22:26:35 +02:00
jagannatharjun
5d4644c4fc Remember sub sort column of transfer list 2021-03-23 22:26:34 +02:00
Chocobo1
a2ef115c66 Simplify progress bar painting 2021-03-23 22:26:31 +02:00
Vladimir Golovnev (Glassez)
1356f200b8 Don't use deprecated QTextCodec 2021-03-23 22:26:30 +02:00
Vladimir Golovnev
3c68896b1d CI: Don't compile on Ubuntu 18.04 2021-03-23 22:26:29 +02:00
Vladimir Golovnev (Glassez)
265da50791 Don't use deprecated features 2021-03-23 22:26:28 +02:00
Vladimir Golovnev (Glassez)
4037143f4e Raise minimum supported Qt version to 5.12 2021-03-23 22:26:26 +02:00
Chocobo1
8cae8ad5c5 Replace parameters in one step
This would avoid the unwanted effect of replacing parameter coming from
another parameter.
2021-03-23 22:26:22 +02:00
Vladimir Golovnev (Glassez)
50bd845682 Initialize torrent status from add torrent params 2021-03-23 22:26:20 +02:00
Vladimir Golovnev (Glassez)
ed5aa07526 CI: Disable libtorrent2 deprecated functions on Travis 2021-03-23 22:26:19 +02:00
Vladimir Golovnev (Glassez)
437b51b3a5 Improve "info hash" handling
Define "torrent ID" concept, which is either a SHA1 hash for torrents of version 1,
or a SHA256 hash (truncated to SHA1 hash length) for torrents of version 2.
Add support for native libtorrent2 info hashes.
2021-03-23 22:26:18 +02:00
Vladimir Golovnev (Glassez)
c2ccc9dfa4 Properly show tracker status for "paused" torrents 2021-03-23 22:26:16 +02:00
Vladimir Golovnev (Glassez)
b2c7d8211f Improve tracker entries handling 2021-03-23 22:26:14 +02:00
Vladimir Golovnev (Glassez)
726455ac3e Don't allow speed plot buffer to overflow 2021-03-23 22:26:13 +02:00
Vladimir Golovnev (Glassez)
ae2bb4efeb Accept "share limits" when adding torrent using WebAPI 2021-03-23 22:26:12 +02:00
Vladimir Golovnev (Glassez)
9971329121 Look for qbittorrent.pdb in installation directory
Pass application directory as PDB search path in SymInitialize.
Otherwise it searches in application working directory so when you
run qBittorrent with working directory other than its installation
one it can't find qbittorent.pdb file and produces broken stacktrace.
2021-03-23 22:26:10 +02:00
Chocobo1
d0ec1c4a86 Expose ToS setting from libtorrent
Closes #14420.
2021-03-23 22:26:08 +02:00
Chocobo1
9c55600d81 Add missing semicolon 2021-03-23 22:26:01 +02:00
Vladimir Golovnev (Glassez)
b45fb74e01 Define template for classes that represent SHA hashes 2021-03-23 21:05:41 +02:00
Vladimir Golovnev (Glassez)
f16c585a77 Drop implicit conversions between InfoHash and QString 2021-03-23 21:05:40 +02:00
Chocobo1
9c664d04ae Remove unused lambda capture 2021-03-23 21:05:38 +02:00
Chocobo1
3d0ca83474 Specify Qt version in TravisCI build script
In homebrew `qt` package is referring to Qt6 instead of Qt5.
2021-03-23 21:05:37 +02:00
Chocobo1
e713ffb064 Properly stop torrent creation if aborted
Closes #11346.
2021-03-23 21:05:37 +02:00
Chocobo1
cf1e61bcf5 Correctly draw the background of progress bar
Closes #12271.
2021-03-23 21:05:36 +02:00
Vladimir Golovnev (Glassez)
42b22d6645 CI: Use custom vcpkg libtorrent port 2021-03-23 21:05:35 +02:00
Vladimir Golovnev (Glassez)
2d607f8c1a Raise minimum libtorrent version to 1.2.12 2021-03-23 21:05:34 +02:00
jagannatharjun
69256905c2 Support sub-sorting in Transferlist 2021-03-23 21:05:32 +02:00
brvphoenix
305316b1fc WebUI: Properly decode strings 2021-03-23 21:05:32 +02:00
Chocobo1
27e222455b Improve detection of filename extension of audio/video files 2021-03-23 21:05:31 +02:00
Michał Kopeć
2b18318e0c Add an option to disable icons in menus 2021-03-23 21:05:30 +02:00
Chocobo1
49cadce253 Enable sponsor button on Github 2021-03-23 21:05:29 +02:00
Juraj Oršulić
f1b908b95b Systemd: wait for mounting of local filesystems 2021-03-23 21:05:28 +02:00
jagannatharjun
4acfcef8da Add a 3-Hour graph 2021-03-23 21:05:27 +02:00
jagannatharjun
69f2196a22 Make SpeedPlotView averager time aware
Previously SpeedPlotView assumed speed is updated per second but the
default value was 1500ms and that can be further changed by the
user, this caused a lot of duplicate data in the calculation of the
graph points. Now Averager averages based on the target duration, resolution
and also takes into account when actually data has arrived.

Also improved resolution of 6-hour graph, previously it was same as 12-hour graph
2021-03-23 21:05:26 +02:00
Chocobo1
b20a3c5b8e Use std::optional to return results 2021-03-23 21:05:25 +02:00
Chocobo1
2c5271b3b2 Fix potential out-of-bounds access 2021-03-23 21:05:24 +02:00
Si Yong Kim
7696895a88 Refactor apply button logics on options dialog 2021-03-23 21:05:23 +02:00
Si Yong Kim
c1ae5d2572 Add empty name error handling on new category dialog 2021-03-23 21:05:23 +02:00
Si Yong Kim
0e635c7fdd Add category button on AutomatedRSSDownloader on GUI
Closes #7629
2021-03-23 21:05:22 +02:00
Chocobo1
58345e5bbf Revise getter function for torrrent queue position
This addresses https://github.com/qbittorrent/qBittorrent/pull/14335#issuecomment-774667836

The WebAPI is not affected as a workaround is added.
2021-03-23 21:05:21 +02:00
Chocobo1
89382d4ec2 Apply "Hide infinity values" to ETA column 2021-03-23 21:05:20 +02:00
Chocobo1
372f5af36b Apply "Hide infinity values" to "Down/Up Limit" columns 2021-03-23 21:05:19 +02:00
Chocobo1
f38736729d Apply "Hide zero values" to "Time Active" column 2021-03-23 21:05:18 +02:00
Chocobo1
bf67ef21c6 Clean up coding style 2021-03-23 21:05:18 +02:00
Chocobo1
cfd40adcb5 Show proper string when torrent availability is not available 2021-03-23 21:05:16 +02:00
Vladimir Golovnev (Glassez)
8210f9841e Restart "missing files" torrents after changing location 2021-03-23 21:05:16 +02:00
Vladimir Golovnev (Glassez)
ae3d17ec01 Allow "missing files" torrents to save more resume data 2021-03-23 21:05:15 +02:00
Vladimir Golovnev (Glassez)
349e958be3 Allow change-case-only file renaming on Windows 2021-03-23 21:05:14 +02:00
Chocobo1
42acc75394 Use stable sorting in transfer list 2021-03-23 21:05:12 +02:00
Chocobo1
8b91dcedb0 Use built-in function for configuring file contents 2021-03-23 21:05:11 +02:00
dyumin
a454a0303d Treat errored torrents as finished 2021-03-23 21:05:11 +02:00
Chocobo1
789c6de2e8 Simplify CI script directives 2021-03-23 21:05:10 +02:00
Chocobo1
c2fb51159f Don't trigger Github Actions CI builds after editing a PR's opening post
After dropping "edited" keyword, it is the same as the default.
2021-03-23 21:05:09 +02:00
PriitUring
bfb0afe3cf NSIS: Add Estonian translation
This file was previously not translated.
PR #14331.
2021-03-23 21:05:08 +02:00
Chocobo1
26a2d4f24d Reuse existing code for sorting
This makes the behavior of sorting by TR_SEED_DATE consistent.
2021-03-23 21:05:08 +02:00
Si Yong Kim
f6e88c8c55 Add hyperlink to Transifex on translator list
Closes #12609
2021-03-23 21:05:07 +02:00
Si Yong Kim
51033c212a Remove Hungarian translator email 2021-03-23 21:05:06 +02:00
Chocobo1
16c858cf61 Prolong checking interval for program updates 2021-03-23 21:05:05 +02:00
Chocobo1
0496543fce Improve behavior when using ProgramUpdater class
This is mainly to avoid involving of `sender()` function.
2021-03-23 21:05:04 +02:00
Chocobo1
746e8a7be1 Revise version comparison 2021-03-23 21:05:04 +02:00
Chocobo1
6d301ccf55 Clean up coding style 2021-03-23 21:05:03 +02:00
jagannatharjun
d441b18da0 Disable expand on double click in TorrentContentTreeView
We hook our own actions on double click. Fixes #14269
2021-03-23 21:05:01 +02:00
Vladimir Golovnev (Glassez)
13023ba70a Bump WebAPI version 2021-03-23 21:05:01 +02:00
Alex
ecb7c02d4c Update Portuguese BR NSIS translation (#12376) 2021-03-23 21:05:00 +02:00
slrslr
fd1ac43157 Translating new phrases (#12318)
* Update Czech NSIS translation

Co-authored-by: slrslr <czautohits@gmail.com>
2021-03-23 21:04:59 +02:00
Chocobo1
c6d4a1f7d4 Enlarge "speed limit" icon slightly 2021-03-23 21:04:58 +02:00
Chocobo1
01110690da Don't let "program update" dialog steal focus
And also avoid creating an unnecessary event loop.
Closes #14250.
2021-03-23 21:04:57 +02:00
Chocobo1
c998c7d38d Disable translation of program name 2021-03-23 21:04:56 +02:00
an0n666
230f98da4a Validate HTTPS Tracker Certificate by default 2021-03-23 21:04:56 +02:00
xavier2k6
c86db0004f Change qBittorrent Updater window title 2021-03-23 21:04:54 +02:00
Christoph Rackwitz
e645514c8f Allow tab to escape the text box in "Edit trackers" dialog 2021-03-23 21:04:53 +02:00
Chocobo1
f3c9dbd512 Remove redundant variable declarations 2021-03-23 21:04:52 +02:00
Chocobo1
ef650293e3 Add ability to prioritize selected items by shown file order
Closes #2834.
2021-03-23 21:04:51 +02:00
Chocobo1
05e217537c Move menu actions out of .ui files
This is to move related code together.
2021-03-23 21:04:50 +02:00
Vladimir Golovnev (Glassez)
13cb3b5ca1 Drop extension from generated content folder name
Try to detect whether generated content folder name contains extension
and drop it to avoid possible conflicts between file/folder names.
2021-03-23 21:04:38 +02:00
sledgehammer999
da0b276d5f Bump to 4.3.3 2021-01-19 00:47:04 +02:00
sledgehammer999
2d73bc9e7d Update Changelog 2021-01-19 00:46:29 +02:00
Chocobo1
fdd54fe568 Simplify code for checking free disk space
`QStorageInfo::bytesAvailable()` is guaranteed to return `-1` for an
invalid path.
https://doc.qt.io/qt-5/qstorageinfo.html#bytesAvailable
2021-01-19 00:45:13 +02:00
Chocobo1
e5ce24e55e Improve detection of file extension string 2021-01-19 00:45:06 +02:00
sledgehammer999
d90349709b Sync translations from Transifex and run lupdate 2021-01-17 23:24:02 +02:00
Vladimir Golovnev (Glassez)
adb0fe6582 WebUI: Correctly represent torrent content structure 2021-01-17 22:50:40 +02:00
Chocobo1
5ed81580c9 Add README.md to searchengine folder 2021-01-17 22:50:39 +02:00
Chocobo1
86d6fb86d7 Unify "github actions" artifacts naming scheme 2021-01-17 22:50:38 +02:00
Chocobo1
ddec247d4f Migrate away from deprecated Qt functions
`QString QDateTime::toString(Qt::DateFormat format = Qt::TextDate)` will
be removed in Qt6.
2021-01-17 22:50:37 +02:00
Chocobo1
d431ecbe00 Disable clang "range loop analysis" compiler warning
See: https://github.com/qbittorrent/qBittorrent/pull/13915#issuecomment-739449084
2021-01-17 22:50:36 +02:00
Chocobo1
be929ed88c Set source character sets to UTF-8
This suppress warning C4819.
https://docs.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8?view=msvc-160
2021-01-17 22:50:35 +02:00
Chocobo1
2e1f9bf8be Add script for generating project tarball 2021-01-17 22:50:20 +02:00
lbilli
7fff393b0e On Linux use legacy 'data' directory only as a fallback 2021-01-17 22:47:13 +02:00
sledgehammer999
a669ec49ad Correct copyright attribution
These files were created and edited in their entirety in commit 8db4bde15d
As far as I can tell they were almost entirety rewritten from their original state.
The old copyright attribution is restored and the new author is added too.
2021-01-17 22:47:11 +02:00
Vladimir Golovnev (Glassez)
1880082017 Remove redundant suffix from TorrentHandle class
Originally, it was just a wrapper for libtorrent::torrent_handle class, so it mimicked its name.
It was then transformed into a more complex aggregate, but the name was retained (just by inertia).
Unlike libtorrent::torrent_handle class in whose name "handle" means the pattern used,
it does not matter for qBittorrent classes and just eats up space in the source code.
2021-01-17 22:47:00 +02:00
sledgehammer999
0cbd15890a Merge pull request #14170 from sledgehammer999/use_cxx1z
Use c++1z to enable c++17
2021-01-07 13:40:05 +02:00
sledgehammer999
7fe7c6c277 Use c++1z to enable c++17
It increases compatibility with older qt versions.
2021-01-06 21:54:46 +02:00
jagannatharjun
e4c177fec7 Correctly set items flags in TorrentContentModel
Only set editable flag on item's where editing is handled in the delegate

closes #13515
2021-01-06 21:36:40 +02:00
Chocobo1
77f4e6c2cf Generate version header when configuring project
The basic idea is we create a version header template at
"src/base/version.h.in" and the build systems are expected to replace
strings that are enclosed with @ symbols and generate
"src/base/version.h" for other source files to consume/include.
2021-01-06 21:36:39 +02:00
sledgehammer999
4563b11a2e Bump copyright year 2021-01-06 21:36:37 +02:00
Vladimir Golovnev (Glassez)
cb477f9a29 QMake: Raise minimal macOS target version 2021-01-06 21:36:35 +02:00
Vladimir Golovnev (Glassez)
58ac07667e Use single parameter to accept torrent source 2021-01-06 21:36:34 +02:00
Vladimir Golovnev (Glassez)
74bf3af41c Use std::optional<bool> instead of custom TriStateBool 2021-01-06 21:36:33 +02:00
Vladimir Golovnev (Glassez)
9317071122 Change parseBool() to return optional bool value 2021-01-06 21:36:31 +02:00
Vladimir Golovnev (Glassez)
dab32f2090 Use std::optional instead of boost::optional 2021-01-06 21:36:29 +02:00
Vladimir Golovnev (Glassez)
dc464d4d41 Use nested namespaces definition syntax 2021-01-06 21:36:28 +02:00
Chocobo1
e7e3f6a9db Don't use deprecated locale name 2021-01-06 21:36:27 +02:00
Chocobo1
5a1c4e79b3 Revise store/load state operations of Options Dialog 2021-01-06 21:36:26 +02:00
Chocobo1
c6d9ab6810 Remember dialog sizes
This applies to "About Dialog", "Ban List Options Dialog", "Download From URL Dialog", "IP Subnet
Whitelist Options Dialog", "Search Plugin Select Dialog", "Search Plugin Source Dialog",
"Statistics Dialog", "Speed Limit Dialog" and "Torrent Options Dialog".

Also unifies storing the dialog size under the key "Size".
2021-01-06 21:36:24 +02:00
Chocobo1
d7afad835e Revise SettingsStorage store/load value interface 2021-01-06 21:36:22 +02:00
Chocobo1
8608d7b9da Improve load data behavior of SettingsStorage class
Previously it only handle the case of failed lookup, now it discard
invalid values when deserializing the database from disk.
Also checks whether the data is convertible to the intended type.
2021-01-06 21:36:22 +02:00
Vladimir Golovnev (Glassez)
72970602af Reload "missing files" torrent instead of re-checking 2021-01-06 21:36:20 +02:00
Vladimir Golovnev (Glassez)
86579ca87d Extract torrent reloading logic into separate method 2021-01-06 21:36:19 +02:00
Vladimir Golovnev (Glassez)
e55582124c Drop notification about move storage failed 2021-01-06 21:36:17 +02:00
Vladimir Golovnev (Glassez)
bd8b06c607 Drop notification about move storage finished 2021-01-06 21:36:16 +02:00
Chocobo1
230fedf069 Move parsing of TriStateBool to a static class function 2021-01-06 21:36:14 +02:00
thalieht
7bea10f507 Update "Keep top-level folder" in WebUI options 2021-01-06 21:36:13 +02:00
Chocobo1
7cde969b90 Exclude configure script for "trailing newlines" checking 2021-01-06 21:36:12 +02:00
Chocobo1
a3b8f6880b Migrate away from deprecated AC_OUTPUT macro
The `AC_OUTPUT` has two versions, the deprecated one takes arguments and the other not. Check the
following link for equivalent replacement:
https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.70/html_node/Obsolete-Macros.html#Obsolete-Macros

Also regenerate the configure script with the latest Autoconf 2.70.
2021-01-06 21:35:33 +02:00
Chocobo1
ad79fc8d43 Migrate away from deprecated std::iterator class 2021-01-06 21:31:51 +02:00
Chocobo1
fb4bf94a56 Use function-pointer based signal-slot connection 2021-01-06 21:31:50 +02:00
Chocobo1
1c184944fd Remove unused define 2021-01-06 21:31:49 +02:00
Chocobo1
ec420f6617 Bump project requirement to C++17 2021-01-06 21:31:47 +02:00
Chocobo1
d908227619 Add a thin layer around SettingsStorage class
This new layer would be handy for saving GUI widget states as they don't
need the value cached and they store/load rarely.
2021-01-06 21:31:45 +02:00
sledgehammer999
ac8167410b Add new languages
* Azerbaijani
* Estonian
2021-01-06 21:31:44 +02:00
Vladimir Golovnev (Glassez)
26ce187b30 Don't call non-existent elements
Fixed a regression where the script tries to access elements that no longer
exist on the page, because they were replaced with others by a previous change.
2021-01-06 21:31:43 +02:00
Vladimir Golovnev (Glassez)
2c4e04e537 Don't call non-existent elements
Fixed a regression where the script tries to access elements that no longer
exist on the page, because they were replaced with others by a previous change.
2021-01-06 21:31:42 +02:00
Vladimir Golovnev (Glassez)
b418f65c2f Improve content file/folder names handling
Move files/folders renaming functions to core classes.
Query file/folder for renaming by its current path.
Add ability to rename content folders from WebAPI/WebUI.
2021-01-06 21:31:41 +02:00
Vladimir Golovnev (Glassez)
dd3a8d5d56 Fix folder name extraction functions
It should return empty string if there is no parent folder.
2021-01-06 21:31:39 +02:00
Chocobo1
49e54a55df Capitalize locale names 2021-01-06 21:31:38 +02:00
thalieht
8cd0a7ae85 Group several torrent options into one dialog
Speed limits, share limits and the new options to disable DHT, PeX, LSD per torrent
2021-01-06 21:31:36 +02:00
thalieht
442f0df613 Save fastresume when setting torrent speed limits 2021-01-06 21:31:35 +02:00
thalieht
f9ee5bdb59 Increase maximum global speed limits from ~1 GiB/s to ~2 GiB/s
Closer to the INT_MAX limit of ~2 Billion when multiplied by 1024 for libtorrent
2021-01-06 21:31:33 +02:00
thalieht
b9602cc6ab Convert existing speed dialog to global + alt global limits only 2021-01-06 21:31:25 +02:00
sledgehammer999
abb854a1e6 Bump to 4.3.2 2020-12-27 13:42:41 +02:00
sledgehammer999
4ee17a73d0 Update Changelog 2020-12-27 13:42:40 +02:00
sledgehammer999
faf6e82274 Sync translations from Transifex and run lupdate 2020-12-26 21:18:40 +02:00
Vladimir Golovnev (Glassez)
c08ec1ac5e Allow to add root folder to torrent content 2020-12-26 20:27:34 +02:00
Vladimir Golovnev (Glassez)
cd0b6d9a43 Extract enum serialization/parsing functions 2020-12-26 20:27:33 +02:00
Vladimir Golovnev (Glassez)
b8f1142abe Improve torrent name handling 2020-12-26 20:27:31 +02:00
Chocobo1
78859415d6 Use a helper function to look up stat indexes 2020-12-26 20:27:29 +02:00
Chocobo1
ef92c17192 Don't use removed stat metric in libtorrent 2.0
For now, the metric is not entirely removed due to WebAPI still needs to
access it.
2020-12-26 20:27:28 +02:00
Chocobo1
22f3abc4b5 Initialize stat indices to -1
When the index is initialized it will be set to a number >= 0, so we use
-1 to denote its uninitialized status.
2020-12-26 20:27:27 +02:00
Chocobo1
a56e6294c1 Fix wrong JSON type returned
Fix up 78638a15be.
Closes #14041.
2020-12-26 20:27:26 +02:00
Chocobo1
77909e0093 Don't use default CFLAGS, CXXFLAGS from autotools
Before this commit, autotools will inject `-g -O2` to debug build
(`--enable-debug=yes`) and rendering the result binary useless. This
commit fixes it.
https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.70/html_node/C_002b_002b-Compiler.html

Closes #14032.
2020-12-26 20:27:25 +02:00
Vladimir Golovnev (Glassez)
2c2bb14b2a Fix bug of "move storage job" can be performed multiple times 2020-12-26 20:27:16 +02:00
Chocobo1
73c8b77464 Migrate away from deprecated QVariant comparison operators
Fortunately, serializing to JSON limits the data types to a very small
subset and thus we are able to implement the comparison without much
hassle.

Fix up cba8d83b21.
2020-12-26 20:27:14 +02:00
Alessandro Simonelli
042238db87 NSIS: Fixed italian.nsi after 4.3.0.1 update
Merged my previous fixes (discussed in #13607) with the suggestions by @glassez (#13615).
2020-12-26 20:27:13 +02:00
FranciscoPombal
6e267f8e81 Update coding guidelines policy for include guards 2020-12-26 20:27:11 +02:00
FranciscoPombal
fdc64d9b38 Use #pragma once instead of include guards 2020-12-26 20:27:10 +02:00
Chocobo1
0b42425db5 Add support for allow_idna option
Upstream PR: https://github.com/arvidn/libtorrent/pull/5316
2020-12-26 20:27:08 +02:00
Chocobo1
e5d7738127 Update "HTTPS tracker validation" enablement conditional
https://github.com/arvidn/libtorrent/pull/5313
2020-12-26 20:27:06 +02:00
Chocobo1
422489e2a1 Revise Utils::Version comparison operators 2020-12-26 20:27:04 +02:00
Chocobo1
7de983b4e5 Add operator< for InfoHash class 2020-12-26 20:27:03 +02:00
Chocobo1
e4e55d2a80 Migrate away from deprecated QVariant comparison operators
Another idea would be manually define a custom comparison function for
QVariant. However, having the function would be excessive due to its
limited usage count, also note that we are already casting
various QVariant to its underlying type in existing code.
2020-12-26 20:27:01 +02:00
Vladimir Golovnev (Glassez)
926012ce71 Fix bug of torrents don't save "stopped" state 2020-12-26 20:27:00 +02:00
Chocobo1
487eb554c9 Fix coding style 2020-12-26 20:26:59 +02:00
Chocobo1
5a96e1fc7a Use Qt provided forward declaration header 2020-12-26 20:26:58 +02:00
FranciscoPombal
2fe698ee60 CMake: detect required header for STACKTRACE feature
musl does not provide execinfo.h, so our current stacktrace-related
code cannot be used with it.
2020-12-26 20:26:57 +02:00
Chocobo1
177ac32a5e Use the correct type when referring to info hash 2020-12-26 20:26:55 +02:00
Vladimir Golovnev (Glassez)
5f34d1555b Fix received metadata handling 2020-12-26 20:26:54 +02:00
FranciscoPombal
7cfe68f46c Bump dependency versions in CI
- Bump vcpkg version in GitHub Actions CI
- Bump libtorrent version in Travis CI

Co-authored-by: Vladimir Golovnev <glassez@yandex.ru>
2020-12-26 20:26:53 +02:00
Vladimir Golovnev
f94f4d2391 Drop support for building with libtorrent < 1.2.11
Co-authored-by: Vladimir Golovnev <glassez@yandex.ru>
2020-12-26 20:26:52 +02:00
FranciscoPombal
73b18d7ef3 Update minimum depedency versions 2020-12-26 20:26:51 +02:00
FranciscoPombal
817e9c4747 Fix method invocation on Qt < 5.10
Fixup 0c3fe54b0b
2020-12-26 20:26:48 +02:00
Vladimir Golovnev (Glassez)
28844eff44 Search for existing files in separate thread 2020-12-26 20:26:47 +02:00
Vladimir Golovnev (Glassez)
389664213b Don't rewrite TorrentInfo instance if it's valid 2020-12-26 20:26:46 +02:00
Vladimir Golovnev (Glassez)
953b6fd6f8 Properly handle "Append extension" option changing 2020-12-26 20:26:45 +02:00
Vladimir Golovnev (Glassez)
9b4f3fcbf8 Clean up metadata downloading code 2020-12-26 20:26:43 +02:00
Chocobo1
80743180be Remove unnecessary restriction on input length
Closes #13884.
2020-12-26 20:26:42 +02:00
Chocobo1
b2847b2381 Update URL to libtorrent settings 2020-12-26 20:26:41 +02:00
Chocobo1
eb657ec032 Move "embedded tracker" options to qbt section 2020-12-26 20:26:39 +02:00
Chocobo1
fc2be601df Add links to libtorrent documentation 2020-12-26 20:26:38 +02:00
Chocobo1
5786c7ff11 Lift upper limit of "Max concurrent HTTP announces" option
Closes #13800.
2020-12-26 20:26:37 +02:00
Jesse Chan
4a183dd968 WebAPI: bump version to 2.6.2 2020-12-26 20:26:36 +02:00
Jesse Chan
7c10dba10c WebAPI: allow to attach tags while adding torrents 2020-12-26 20:26:35 +02:00
sledgehammer999
894446d308 Don't try to remove folders for a torrent without metadata 2020-12-26 20:26:34 +02:00
sledgehammer999
47e9c5ac08 Fix status of torrents without metadata 2020-12-26 20:26:33 +02:00
Chocobo1
7f47ac11f1 Add libtorrent 2.0 to TravisCI script
Also bumping to ubuntu focal as libtorrent requires boost >= 1.66.
2020-12-26 20:26:32 +02:00
Chocobo1
67b17891fa Simplify the calculation of speed graph scale 2020-12-26 20:26:31 +02:00
Chocobo1
dd5b7ba05b Avoid potential rounding to integer issues 2020-12-26 20:26:30 +02:00
Chocobo1
61aa4d9f1c Fix coding style 2020-12-26 20:26:29 +02:00
Chocobo1
6e924b668e Fix availability value
Closes #13869.
Fix up 02f19bfbee.
2020-12-26 20:26:27 +02:00
FranciscoPombal
618ce33fa0 Detect .ts file issues with file health workflow
Also adjust newlines to improve output
2020-12-26 20:26:26 +02:00
Chocobo1
ac413c76b9 Update to use latest macOS image for TravisCI
The default version is so outdated that it needs to rebuild many
dependencies. Now we bump it to the latest version so that it can use
prebuilt packages.
2020-12-26 20:26:25 +02:00
Chocobo1
f266184514 Add ability to use 'shift+delete' to delete torrents in WebUI
Closes #13827.
2020-12-26 20:26:24 +02:00
Chocobo1
8c48bf4a70 Fix wrong data used for comparison
In torrent transfer list we should use underlying data for sorting, not
displayed values.

Closes #13818.
2020-12-26 20:26:23 +02:00
Andrei Stepanov
8bee69c9fc NSIS: Update Russian translation 2020-12-26 20:26:14 +02:00
sledgehammer999
5876886345 Bump to 4.3.1 2020-11-25 13:49:50 +02:00
sledgehammer999
0392bfce3c Update Changelog 2020-11-25 13:49:48 +02:00
sledgehammer999
c66cf43d6a Sync translations from Transifex and run lupdate 2020-11-24 17:04:07 +02:00
Chocobo1
7515afc058 Pin github actions scripts to major versions
> Using the specific major action version allows you to receive critical
> fixes and security patches while still maintaining compatibility. It
> also assures that your workflow should still work.
https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstepsuses
2020-11-24 16:14:16 +02:00
Thomas De Rocker
5fcfcc901e Fix confusion in date format description (#1)
* Update automatedrssdownloader.ui

* Update rssDownloader.html (#2)
2020-11-24 16:14:15 +02:00
Vladimir Golovnev (Glassez)
1728c16580 Improve coding style 2020-11-24 16:14:14 +02:00
Thomas De Rocker
d3f46452a9 Update dutch.nsi
As referenced in https://github.com/qbittorrent/qBittorrent/pull/13615
and suggested by @glassez 
- Use the word qBittorrent when it is mentioned for the first time within a string.
- If the string contains other references to qBittorrent, then use it (rather than repeating qBittorrent).

Line 24 is the updated Dutch translation for the recently updated English source string (may 2020)
2020-11-24 16:14:09 +02:00
sledgehammer999
7092a98c93 Add Latgalian translation
Closes #12415
2020-11-24 16:14:08 +02:00
sledgehammer999
8e19f66b4f Update .desktop file translations 2020-11-24 16:14:06 +02:00
Chocobo1
b6ab2abf3f Don't use deprecated torrent state "allocating"
Closes #13737.
2020-11-24 16:14:05 +02:00
Vladimir Golovnev (Glassez)
3edaaa30c9 Handle torrent "paused" state at application level 2020-11-24 16:14:04 +02:00
FranciscoPombal
38efff461e Add GitHub Actions file health workflow 2020-11-24 16:14:03 +02:00
FranciscoPombal
2179148b8d Make sure there are no empty files 2020-11-24 16:14:02 +02:00
FranciscoPombal
f92c4c0a40 Fix extra/missing trailing new lines in files 2020-11-24 16:14:00 +02:00
FranciscoPombal
1e7f792dbb Fix trailing whitespace in multiple files
Also fix formatting of CODING_GUIDELINES.md
2020-11-24 16:13:59 +02:00
FranciscoPombal
1d4af505c2 Encode files in UTF-8 without BOM 2020-11-24 16:13:57 +02:00
Chocobo1
baa609b713 Remove unused function 2020-11-24 16:13:55 +02:00
Chocobo1
bc20cf9ad7 Remove redundant semicolon 2020-11-24 16:13:53 +02:00
Chocobo1
7d3ecfa9a6 Allow adding torrents using "Paste" key sequence
Closes #13685.
2020-11-24 16:13:52 +02:00
NotTsunami
88a90ed7d4 Fix mingw64 build error
mingw64 defines interface, so revert back to previous naming scheme

Fixes: 87864531ab
Closes #13649
2020-11-24 16:13:51 +02:00
jagannatharjun
3e540b3f51 Align integer data to right in torrent content view 2020-11-24 16:13:50 +02:00
jagannatharjun
87e1661bd5 Rename TR_PROGRESS header in TransferList
Closes #13665
2020-11-24 16:13:49 +02:00
Chocobo1
f82a4051af Remove outdated information 2020-11-24 16:13:47 +02:00
Vladimir Golovnev (Glassez)
5730e917a1 Fix torrent state calculation 2020-11-24 16:13:46 +02:00
Vladimir Golovnev (Glassez)
5e7d7c2ef0 Don't resume "paused" torrents when checking by libtorrent 2020-11-24 16:13:45 +02:00
jagannatharjun
2b6e1953d7 Use ProgressbarDelegate for drawing progressbar in PropListDelegate
Also directly provide display data from model rather then generating it in delegate
2020-11-24 16:13:43 +02:00
jagannatharjun
6fc50f4169 Use ProgressBarDelegate for drawing progressbar in TransferListView 2020-11-24 16:13:42 +02:00
jagannatharjun
40d7a53695 Implement ProgressBarDelegate 2020-11-24 16:13:41 +02:00
Chocobo1
7e89893454 Add support for tracker scrape in libtorrent 2.0 2020-11-24 16:13:39 +02:00
Chocobo1
d83f09e731 Migrate away from deprecated functions in libtorrent 2.0 2020-11-24 16:13:38 +02:00
Chocobo1
36575b225d Improve compatibility with libtorrent 2.0
In libtorrent 2.0, the `connection_type` was changed to a flag type and
hence it cannot be used in a switch statement directly. Also our use of
`connection_type` is limited so that a single equality comparison
would cover all of our use cases.
2020-11-24 16:13:36 +02:00
jagannatharjun
fe0ea843e0 Fix crash when clicked outside the table of torrent content view
Closes #13645
2020-11-24 16:13:35 +02:00
FranciscoPombal
a8911f8136 Clarify protocol choice label 2020-11-24 16:13:34 +02:00
FranciscoPombal
c5ef1a0207 Update "GitHub Actions" CI actions versions 2020-11-24 16:13:32 +02:00
brvphoenix
102d628c0a Fix the issue that IPv6 address can't be banned
The ban action doesn't depend on ipfilter.
2020-11-24 16:13:31 +02:00
FranciscoPombal
6ea3acdaea Expose contentPath in WebAPI torrents/info
Bump WebAPI version to 2.6.1
2020-11-24 16:13:30 +02:00
Chocobo1
621578353d Remove redundant checking before remove 2020-11-24 16:13:29 +02:00
Chocobo1
ca776c3036 Fix class name 2020-11-24 16:13:28 +02:00
Chocobo1
9d27eb3b57 Move qHash helper for libtorrent types to its own file 2020-11-24 16:13:26 +02:00
Vladimir Golovnev (Glassez)
9171dffe97 Prevent resume data to be saved for removed torrent 2020-11-24 16:13:25 +02:00
thalieht
f919d4f5bf Fix toggling advanced option in WebUI
option "Disallow connection to peers on privileged ports"
2020-11-24 16:13:24 +02:00
Chocobo1
59afc7c520 Avoid settings being reset via WebAPI
Closes #13585.
2020-11-24 16:13:23 +02:00
Chocobo1
f02b65b866 Fix typos 2020-11-24 16:13:19 +02:00
Andrei Stepanov
891c471160 Fix typo in connection.cpp
limiation -> limitation
2020-11-24 16:13:17 +02:00
Sepro
f49f5ba9a1 Place WebUI RSS description in sandboxed iframe 2020-11-24 16:10:40 +02:00
NotTsunami
539b3b7c3e Remove branches block from Travis config
This block has no significance because these branches are stale and
builds will not be triggered on these branches.
2020-11-24 16:10:39 +02:00
NotTsunami
83ce285138 Disallow CMake build failures on Travis
Our CMake configuration has matured over time, thus require CMake
builds to require successful builds on Travis CI.
2020-11-24 16:10:30 +02:00
sledgehammer999
260e48b705 Correct the Changelog entry 2020-10-22 14:48:11 +03:00
sledgehammer999
f09ee1b398 Bump to 4.3.0.1 2020-10-22 09:29:00 +03:00
sledgehammer999
e3c2266611 Update Changelog 2020-10-22 09:22:54 +03:00
bovirus
53fb6220c5 NSIS: Update Italian translation 2020-10-22 09:21:00 +03:00
sledgehammer999
34e6b73374 Close parentheses in Changelog entry 2020-10-19 01:48:52 +03:00
sledgehammer999
b925cffddb Bump to 4.3.0 2020-10-18 22:41:04 +03:00
sledgehammer999
3595626eff Update Changelog 2020-10-18 22:41:03 +03:00
357 changed files with 48690 additions and 108471 deletions

View File

@@ -3,7 +3,7 @@ version: '{branch}-{build}'
# Do not build on tags (GitHub only)
skip_tags: true
image: Visual Studio 2019
image: Visual Studio 2017
branches:
except: # blacklist
@@ -13,8 +13,8 @@ environment:
REPO_DIR: &REPO_DIR c:\qbittorrent
CACHE_DIR: &CACHE_DIR c:\qbt_cache
QBT_VER_URL: https://builds.shiki.hu/appveyor/version_64
QBT_LIB_URL: https://builds.shiki.hu/appveyor/qbt_libraries_64.7z
QBT_VER_URL: https://builds.shiki.hu/appveyor/version
QBT_LIB_URL: https://builds.shiki.hu/appveyor/qbt_libraries.7z
# project directory
clone_folder: *REPO_DIR
@@ -38,12 +38,12 @@ install:
appveyor DownloadFile "%QBT_LIB_URL%" -FileName "c:\qbt_lib.7z" && 7z x "c:\qbt_lib.7z" -o"%CACHE_DIR%" > nul &&
COPY "c:\version_new" "%CACHE_DIR%\version")
# Qt stay compressed in cache
- 7z x "%CACHE_DIR%\qt5_64.7z" -o"c:\qbt" > nul
- 7z x "%CACHE_DIR%\qt5_32.7z" -o"c:\qbt" > nul
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;
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
- SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom;
# setup project
- COPY /Y "%CACHE_DIR%\conf.pri" "%REPO_DIR%"
# workarounds

19
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,19 @@
**Please provide the following information**
### qBittorrent version and Operating System
(type here)
### If on linux, libtorrent-rasterbar and Qt version
(type here)
### What is the problem
(type here)
### What is the expected behavior
(type here)
### Steps to reproduce
(type here)
### Extra info(if any)
(type here)

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 -->

View File

@@ -1,20 +0,0 @@
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
url: "https://github.com/qbittorrent/qBittorrent/wiki/"
-
about: "Please ask questions related to usage/setup/support/non-issue development discussion in the Discussions section"
name: Question
url: "https://github.com/qbittorrent/qBittorrent/discussions"
-
about: "Alternatively, ask on the official forum"
name: Question
url: "http://forum.qbittorrent.org/"
-
about: "Alternatively, use the subreddit"
name: Question
url: "https://www.reddit.com/r/qBittorrent/"

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

@@ -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 :) -->

18
.github/SUPPORT.md vendored
View File

@@ -1,18 +0,0 @@
# Support Resources
The issue tracker is only for bug reports/feature requests related to the project itself.
Please do not use the issue tracker for questions about general program usage,
how BitTorrent (the protocol) works in general, etc.
For such questions, use one of the following community support resources:
* The [discussions section][discussions-url]
* The official forum [official forum][forum-url]
* The [qBittorrent subreddit][subreddit-url]
[discussions-url]: https://github.com/qbittorrent/qBittorrent/discussions
[forum-url]: http://forum.qbittorrent.org/
[subreddit-url]: https://www.reddit.com/r/qBittorrent/

View File

@@ -7,10 +7,10 @@ name: GitHub Actions CI
on: [pull_request, push]
env:
VCPKG_COMMIT: 8f03e2264da6b95fa5b01dd89cdd5b499458d428
VCPKG_COMMIT: e4ce66eecfd3e5cca5eac06c971921bf8e37cf88
VCPKG_DEST_MACOS: /Users/runner/qbt_tools/vcpkg
VCPKG_DEST_WIN: C:\qbt_tools\vcpkg
LIBTORRENT_VERSION_TAG: v1.2.13
LIBTORRENT_VERSION_TAG: v1.2.12
jobs:
@@ -39,15 +39,8 @@ jobs:
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
libboost-dev libboost-chrono-dev libboost-random-dev libboost-system-dev \
libqt5svg5-dev qtbase5-dev qttools5-dev
- name: install libtorrent from source
run: |
@@ -69,16 +62,12 @@ jobs:
--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
@@ -98,13 +87,13 @@ jobs:
- name: checkout repository
uses: actions/checkout@v2
# - ninja is needed for building qBittorrent (because it's preferable, not a hard requirement)
# - ninja is needed for building qBittorrent (because it's preferrable, 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
uses: lukka/run-vcpkg@v4
with:
vcpkgDirectory: ${{ env.VCPKG_DEST_WIN }}
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
@@ -131,11 +120,13 @@ jobs:
"qt5-winextras:x64-windows-static-release"
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe upgrade `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--overlay-ports=${{ github.workspace }}/vcpkg `
--no-dry-run
foreach($package in $packages)
{
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg.exe install $package `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--overlay-ports=${{ github.workspace }}/vcpkg `
--clean-after-build
}
@@ -184,7 +175,7 @@ jobs:
- name: checkout repository
uses: actions/checkout@v2
# - ninja is needed for building qBittorrent (because it's preferable, not a hard requirement)
# - ninja is needed for building qBittorrent (because it's preferrable, 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
@@ -192,7 +183,7 @@ jobs:
brew install automake ninja
- name: setup vcpkg (cached, if possible)
uses: lukka/run-vcpkg@v7
uses: lukka/run-vcpkg@v4
with:
vcpkgDirectory: ${{ env.VCPKG_DEST_MACOS }}
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
@@ -206,13 +197,6 @@ jobs:
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 = `
@@ -224,11 +208,13 @@ jobs:
"qt5-macextras:x64-osx-release"
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg upgrade `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--overlay-ports=${{ github.workspace }}/vcpkg `
--no-dry-run
foreach($package in $packages)
{
${{ env.RUNVCPKG_VCPKG_ROOT }}/vcpkg install $package `
--overlay-triplets=${{ github.workspace }}/triplets_overlay `
--overlay-ports=${{ github.workspace }}/vcpkg `
--clean-after-build
}

View File

@@ -47,13 +47,12 @@ addons:
# 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]
- [qtbase5-dev, libqt5svg5-dev, qttools5-dev]
- zlib1g-dev
before_install:
@@ -76,11 +75,6 @@ before_install:
# 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
- |
@@ -131,7 +125,7 @@ install:
pushd "$HOME"
git clone --single-branch --branch RC_1_2 https://github.com/arvidn/libtorrent.git
cd libtorrent
git checkout tags/v1.2.13
git checkout tags/v1.2.12
cmake \
-DCMAKE_BUILD_TYPE=Release \
@@ -146,7 +140,7 @@ install:
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 checkout tags/v2.0.2
git submodule update --init --recursive
cmake \

View File

@@ -12,9 +12,9 @@ project(qBittorrent
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
# version requirements - older vesions may work, but you are on your own
set(minBoostVersion 1.65)
set(minQtVersion 5.14)
set(minQtVersion 5.11)
set(minOpenSSLVersion 1.1.1)
set(minLibtorrentVersion 1.2.13)
set(minLibtorrentVersion 1.2.12)
set(minZlibVersion 1.2.11)
# features (some are platform-specific)

130
Changelog
View File

@@ -1,6 +1,130 @@
Unreleased - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.4.0
- FEATURE: Add support for creating v2 torrents(requires libtorrent 2.0.x) (Chocobo1)
- FEATURE: Expose libtorrent hashing_threads settings (Anton Bershanskiy)
Sun May 02 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.5
- BUGFIX: Move cursor to the end when autofilling URL/hash in "Download from URLs" dialog (Chocobo1)
- BUGFIX: Sort invalid QDateTime values after valid values (Chocobo1)
- BUGFIX: Fix tabChangesFocus attribute in "Edit trackers" dialog (Christoph Rackwitz)
- BUGFIX: Update DynDNS register url (zhuangzi926)
- BUGFIX: Handle "not enough disk space" error more graciously (glassez)
- BUGFIX: Correctly draw progress background with stylesheet (jagannatharjun)
- WEBUI: Fix magnet url from the search facility (Chocobo1)
- WEBUI: Revise folder monitoring functions (Chocobo1)
- WEBUI: Fix magnet url from the browser (brvphoenix)
- WEBUI: Allow to specify file indexes in torrents/files API (glassez)
- WINDOWS: NSIS: Allow more strings to translated (bovirus, Chocobo1)
- WINDOWS: NSIS: Update Italian, German, Estonian, Russian, PortugueseBR translations (bovirus, Henry Water, PriitUring, Долматов Алексей, Felipe)
- LINUX: Fix D-Bus Notification `desktop-entry` field (Chocobo1)
- MACOS: Don't use executable name as CFBundleName value (Nick Korotysh)
- OTHER: Lower Qt requirement to 5.11 (sledgehammer999)
- OTHER: Clarify that the license is GPLv2+ (sledgehammer999)
Wed Mar 24 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.4.1
- BUGFIX: Correctly draw progress bar (glassez)
- WEBUI: Fix javascript code which broke the UI (Chocobo1)
Tue Mar 23 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.4
- FEATURE: Add ability to prioritize selected items by shown file order (Chocobo1)
- FEATURE: Allow tab to escape the text box in "Edit trackers" dialog (Christoph Rackwitz)
- FEATURE: Support sub-sorting in Transferlist (jagannatharjun)
- FEATURE: Expose ToS setting from libtorrent (Chocobo1)
- FEATURE: Improve tracker entries handling (glassez)
- BUGFIX: Drop extension from generated content folder name (glassez)
- BUGFIX: Change qBittorrent Updater window title (xavier2k6)
- BUGFIX: Validate HTTPS Tracker Certificate by default (an0n666)
- BUGFIX: Don't let "program update" dialog steal focus (Chocobo1)
- BUGFIX: Disable expand on double click in TorrentContentTreeView (jagannatharjun)
- BUGFIX: Add hyperlink to Transifex on translator list (Si Yong Kim)
- BUGFIX: Enlarge "speed limit" icon slightly (Chocobo1)
- BUGFIX: Don't prevent system sleep due to errored torrents (dyumin)
- BUGFIX: Use stable sorting in transfer list (Chocobo1)
- BUGFIX: Allow "missing files" torrents to save more resume data (glassez)
- BUGFIX: Restart "missing files" torrents after changing location (glassez)
- BUGFIX: Show proper string when torrent availability is not available (Chocobo1)
- BUGFIX: Apply "Hide zero/infinity values" to "Time Active", "Down/Up Limit" and ETA columns (Chocobo1)
- BUGFIX: Fix potential out-of-bounds access (Chocobo1)
- BUGFIX: Make SpeedPlotView averager time aware (jagannatharjun)
- BUGFIX: Add a 3-Hour graph (jagannatharjun)
- BUGFIX: Add an option to disable icons in menus (always disabled on MacOS) (Michał Kopeć)
- BUGFIX: Improve detection of filename extension of audio/video files (Chocobo1)
- BUGFIX: Various drawing improvements of progress bar (Chocobo1)
- BUGFIX: Properly stop torrent creation if aborted (Chocobo1)
- BUGFIX: Replace external program parameters in one step (Chocobo1)
- BUGFIX: Improve "save resume data" handling (glassez)
- BUGFIX: Fix bad IPv6 address format for outgoingInterfaces (treysis)
- WEBUI: Properly decode strings (brvphoenix)
- WEBUI: Accept "share limits" when adding torrent using WebAPI (glassez)
- WEBUI: Add seeding time to the active time column (thalieht)
- WEBUI: Fix incorrect seeding time string in General tab (thalieht)
- WEBUI: Allow >100 days in WebUI function "friendlyDuration" (thalieht)
- WEBUI: Avoid decoding strings repeatedly (brvphoenix)
- RSS: Add category button on AutomatedRSSDownloader on GUI (Si Yong Kim)
- WINDOWS: NSIS: Update Czech translation (slrslr)
- WINDOWS: NSIS: Update Portuguese BR translation (Alex)
- WINDOWS: NSIS: Add Estonian translation (PriitUring)
- WINDOWS: Allow change-case-only file renaming (glassez)
- LINUX: Systemd: wait for mounting of local filesystems (Juraj Oršulić)
- OTHER: Raise minimum libtorrent version to 1.2.12 (glassez)
- OTHER: Raise minimum Qt version to 5.12 (glassez)
Tue Jan 19 2021 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.3
- FEATURE: New languages: Azerbaijani, Estonian
- BUGFIX: Unify global speed dialogs for normal/alternative speeds (thalieht)
- BUGFIX: Increase maximum global speed limits ~2 GiB/s (thalieht)
- BUGFIX: Save fastresume when setting torrent speed limits (thalieht)
- BUGFIX: Group several torrent options into one dialog (thalieht)
- BUGFIX: Capitalize locale names (Chocobo1)
- BUGFIX: Improve content file/folder names handling (glassez)
- BUGFIX: Drop notification about move storage finished or failed (glassez)
- BUGFIX: Reload "missing files" torrent instead of re-checking (glassez)
- BUGFIX: Remember dialog sizes (Chocobo1)
- BUGFIX: Improve detection of file extension string (Chocobo1)
- WEBUI: Don't call non-existent elements (glassez)
- WEBUI: Update "Keep top-level folder" in WebUI options (thalieht)
- MACOS: QMake: Raise minimal macOS target version to 10.14 (glassez)
- LINUX: Use legacy 'data' directory only as a fallback (lbilli)
- OTHER: Bump project requirement to C++17 (Chocobo1)
Sun Dec 27 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.2
- FEATURE: Allow to add root folder to torrent content (glassez)
- FEATURE: "HTTPS tracker validation" option is available on all platforms with latest libtorrent (Chocobo1)
- FEATURE: Option for supporting internationalized domain names (IDNs) (Chocobo1)
- BUGFIX: Fix broken sorting on some columns (Chocobo1)
- BUGFIX: Fix availability per file value (Chocobo1)
- BUGFIX: Fix status of torrents without metadata (sledgehammer999)
- BUGFIX: Don't try to remove folders for a torrent without metadata (sledgehammer999)
- BUGFIX: Lift upper limit of "Max concurrent HTTP announces" option (Chocobo1)
- BUGFIX: Add links to libtorrent documentation (Chocobo1)
- BUGFIX: Move "embedded tracker" options to qbt section (Chocobo1)
- BUGFIX: Properly handle "Append extension" option changing (glassez)
- BUGFIX: Correctly save paused torrent state (glassez)
- BUGFIX: Fix bug of "move storage job" can be performed multiple times (glassez)
- WEBUI: Add ability to use 'shift+delete' to delete torrents (Chocobo1)
- WEBUI: Allow to attach tags while adding torrents (Jesse Chan)
- WEBUI: Bump version to 2.6.2 (Jesse Chan)
- WEBUI: Remove unnecessary restriction on input length (Chocobo1)
- WINDOWS: NSIS: Update Russian translation (Andrei Stepanov)
- WINDOWS: NSIS: Update Italian translation (Alessandro Simonelli)
- OTHER: Drop support for building with libtorrent < 1.2.11 (Vladimir Golovnev)
Wed Nov 25 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.1
- FEATURE: Allow progress bar styling from custom themes (jagannatharjun)
- FEATURE: Allow adding torrents using "Paste" key sequence (Chocobo1)
- FEATURE: Add Latgalian translation (sledgehammer999)
- BUGFIX: Prevent resume data to be saved for removed torrent (glassez)
- BUGFIX: Clarify connection protocol choice label (FranciscoPombal)
- BUGFIX: Fix crash when clicked outside the table of torrent content view (jagannatharjun)
- BUGFIX: Don't resume "paused" torrents when put into "checking" state by libtorrent (glassez)
- BUGFIX: Fix torrent state calculation (glassez)
- BUGFIX: Align integer data to right in torrent content view (jagannatharjun)
- WEBUI: Place WebUI RSS description in sandboxed iframe (Sepro)
- WEBUI: Avoid settings being reset via WebAPI (Chocobo1)
- WEBUI: Fix toggling advanced option in WebUI (thalieht)
- WEBUI: Expose contentPath in WebAPI torrents/info (FranciscoPombal)
- WEBUI: Fix the issue that IPv6 address can't be banned (brvphoenix)
- RSS: Fix confusion in date format description (Thomas De Rocker)
- WINDOWS: Update dutch.nsi (Thomas De Rocker)
- LINUX: Update .desktop file translations (sledgehammer999)
Thu Oct 22 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.0.1
- WINDOWS: NSIS: Update Italian translation (bovirus)
Sun Oct 18 2020 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.3.0
- FEATURE: Many UI elements colors are themeable now (jagannatharjun)

View File

@@ -5,13 +5,13 @@ qBittorrent - A BitTorrent client in C++ / Qt
- Boost >= 1.65
- libtorrent-rasterbar >= 1.2.13 (by Arvid Norberg)
- libtorrent-rasterbar >= 1.2.12 (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.12
- zlib >= 1.2.11

View File

@@ -17,7 +17,7 @@ macro(qbt_common_config)
)
target_compile_definitions(qbt_common_cfg INTERFACE
QT_DISABLE_DEPRECATED_BEFORE=0x050e00
QT_DEPRECATED_WARNINGS
QT_NO_CAST_TO_ASCII
QT_NO_CAST_FROM_BYTEARRAY
QT_USE_QSTRINGBUILDER
@@ -37,7 +37,7 @@ macro(qbt_common_config)
)
endif()
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang"))
if ((CXX_COMPILER_ID STREQUAL "GNU") OR (CXX_COMPILER_ID STREQUAL "Clang") OR (CXX_COMPILER_ID STREQUAL "AppleClang"))
target_compile_options(qbt_common_cfg INTERFACE
-Wall
-Wextra
@@ -59,7 +59,7 @@ macro(qbt_common_config)
endif()
endif()
if ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang"))
if ((CXX_COMPILER_ID STREQUAL "Clang") OR (CXX_COMPILER_ID STREQUAL "AppleClang"))
target_compile_options(qbt_common_cfg INTERFACE
-Wno-range-loop-analysis
)
@@ -79,8 +79,6 @@ macro(qbt_common_config)
target_compile_options(qbt_common_cfg INTERFACE
/guard:cf
/utf-8
# https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
/Zc:__cplusplus
)
target_link_options(qbt_common_cfg INTERFACE
/guard:cf

627
configure vendored
View File

@@ -1,12 +1,11 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for qbittorrent v4.4.0alpha.
# Generated by GNU Autoconf 2.70 for qbittorrent v4.3.5.
#
# Report bugs to <bugs.qbittorrent.org>.
#
#
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
# Inc.
# Copyright (C) 1992-1996, 1998-2017, 2020 Free Software Foundation, Inc.
#
#
# This configure script is free software; the Free Software Foundation
@@ -611,8 +610,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='qbittorrent'
PACKAGE_TARNAME='qbittorrent'
PACKAGE_VERSION='v4.4.0alpha'
PACKAGE_STRING='qbittorrent v4.4.0alpha'
PACKAGE_VERSION='v4.3.5'
PACKAGE_STRING='qbittorrent v4.3.5'
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
PACKAGE_URL='https://www.qbittorrent.org/'
@@ -638,6 +637,7 @@ openssl_LIBS
openssl_CFLAGS
libtorrent_LIBS
libtorrent_CFLAGS
BOOST_SYSTEM_LIB
BOOST_LDFLAGS
BOOST_CPPFLAGS
Qt5Svg_LIBS
@@ -757,6 +757,7 @@ enable_webui
enable_qt_dbus
with_boost
with_boost_libdir
with_boost_system
'
ac_precious_vars='build_alias
host_alias
@@ -1329,7 +1330,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures qbittorrent v4.4.0alpha to adapt to many kinds of systems.
\`configure' configures qbittorrent v4.3.5 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1400,7 +1401,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of qbittorrent v4.4.0alpha:";;
short | recursive ) echo "Configuration of qbittorrent v4.3.5:";;
esac
cat <<\_ACEOF
@@ -1434,6 +1435,10 @@ Optional Packages:
this parameter only if default library detection
fails and you know exactly where your boost
libraries are located.
--with-boost-system[=special-lib]
use the System library from boost - it is possible
to specify a certain library for the linker e.g.
--with-boost-system=boost_system-gcc-mt
Some influential environment variables:
CC C compiler command
@@ -1450,7 +1455,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.11, overriding pkg-config
Qt5Svg_CFLAGS
C compiler flags for Qt5Svg, overriding pkg-config
Qt5Svg_LIBS linker flags for Qt5Svg, overriding pkg-config
@@ -1533,10 +1538,10 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
qbittorrent configure v4.4.0alpha
generated by GNU Autoconf 2.71
qbittorrent configure v4.3.5
generated by GNU Autoconf 2.70
Copyright (C) 2021 Free Software Foundation, Inc.
Copyright (C) 2020 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
@@ -1624,6 +1629,53 @@ fi
as_fn_set_status $ac_retval
} # ac_fn_cxx_try_compile
# ac_fn_cxx_try_link LINENO
# -------------------------
# Try to link conftest.$ac_ext, and return whether this succeeded.
ac_fn_cxx_try_link ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext
if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_link") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
test -x conftest$ac_exeext
}
then :
ac_retval=0
else $as_nop
printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
# Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
# created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
# interfere with the next link command; also delete a directory that is
# left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_cxx_try_link
ac_configure_args_raw=
for ac_arg
do
@@ -1648,8 +1700,8 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by qbittorrent $as_me v4.4.0alpha, which was
generated by GNU Autoconf 2.71. Invocation command line was
It was created by qbittorrent $as_me v4.3.5, which was
generated by GNU Autoconf 2.70. Invocation command line was
$ $0$ac_configure_args_raw
@@ -3463,10 +3515,7 @@ else
CFLAGS=
fi
fi
ac_prog_cc_stdc=no
if test x$ac_prog_cc_stdc = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
printf %s "checking for $CC option to enable C11 features... " >&6; }
if test ${ac_cv_prog_cc_c11+y}
then :
@@ -3490,28 +3539,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
done
rm -f conftest.$ac_ext
CC=$ac_save_CC
fi
if test "x$ac_cv_prog_cc_c11" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
else $as_nop
if test "x$ac_cv_prog_cc_c11" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
CC="$CC $ac_cv_prog_cc_c11"
fi
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
# AC_CACHE_VAL
ac_prog_cc_stdc_options=
case "x$ac_cv_prog_cc_c11" in #(
x) :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; } ;; #(
xno) :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; } ;; #(
*) :
ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c11"
CC="$CC$ac_prog_cc_stdc_options"
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } ;;
esac
if test "x$ac_cv_prog_cc_c11" != xno
then :
ac_prog_cc_stdc=c11
fi
fi
if test x$ac_prog_cc_stdc = xno
then :
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
printf %s "checking for $CC option to enable C99 features... " >&6; }
if test ${ac_cv_prog_cc_c99+y}
@@ -3522,9 +3571,9 @@ else $as_nop
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_c_conftest_c99_program
$ac_c_conftest_c89_program
_ACEOF
for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99=
for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc1x -qlanglvl=extc99
do
CC="$ac_save_CC $ac_arg"
if ac_fn_c_try_compile "$LINENO"
@@ -3536,28 +3585,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
done
rm -f conftest.$ac_ext
CC=$ac_save_CC
fi
if test "x$ac_cv_prog_cc_c99" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
else $as_nop
if test "x$ac_cv_prog_cc_c99" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
CC="$CC $ac_cv_prog_cc_c99"
fi
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
# AC_CACHE_VAL
ac_prog_cc_stdc_options=
case "x$ac_cv_prog_cc_c99" in #(
x) :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; } ;; #(
xno) :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; } ;; #(
*) :
ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c99"
CC="$CC$ac_prog_cc_stdc_options"
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } ;;
esac
if test "x$ac_cv_prog_cc_c99" != xno
then :
ac_prog_cc_stdc=c99
fi
fi
if test x$ac_prog_cc_stdc = xno
then :
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
printf %s "checking for $CC option to enable C89 features... " >&6; }
if test ${ac_cv_prog_cc_c89+y}
@@ -3570,7 +3619,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_c_conftest_c89_program
_ACEOF
for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
-Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
do
CC="$ac_save_CC $ac_arg"
if ac_fn_c_try_compile "$LINENO"
@@ -3582,25 +3632,34 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
done
rm -f conftest.$ac_ext
CC=$ac_save_CC
fi
# AC_CACHE_VAL
ac_prog_cc_stdc_options=
case "x$ac_cv_prog_cc_c89" in #(
x) :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; } ;; #(
xno) :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; } ;; #(
*) :
ac_prog_cc_stdc_options=" $ac_cv_prog_cc_c89"
CC="$CC$ac_prog_cc_stdc_options"
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } ;;
esac
if test "x$ac_cv_prog_cc_c89" != xno
then :
ac_prog_cc_stdc=c89
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
else $as_nop
ac_prog_cc_stdc=no
ac_cv_prog_cc_stdc=no
fi
if test "x$ac_cv_prog_cc_c89" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
else $as_nop
if test "x$ac_cv_prog_cc_c89" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
CC="$CC $ac_cv_prog_cc_c89"
fi
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
ac_prog_cc_stdc=c89
fi
fi
ac_ext=c
@@ -3950,22 +4009,19 @@ else
CXXFLAGS=
fi
fi
ac_prog_cxx_stdcxx=no
if test x$ac_prog_cxx_stdcxx = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5
printf %s "checking for $CXX option to enable C++11 features... " >&6; }
if test ${ac_cv_prog_cxx_11+y}
if test ${ac_cv_prog_cxx_cxx11+y}
then :
printf %s "(cached) " >&6
else $as_nop
ac_cv_prog_cxx_11=no
ac_cv_prog_cxx_cxx11=no
ac_save_CXX=$CXX
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_cxx_conftest_cxx11_program
_ACEOF
for ac_arg in '' -std=gnu++11 -std=gnu++0x -std=c++11 -std=c++0x -qlanglvl=extended0x -AA
for ac_arg in '' -std=gnu++11 -std=c++11 -std=gnu++0x -std=c++0x -qlanglvl=extended0x -AA
do
CXX="$ac_save_CXX $ac_arg"
if ac_fn_cxx_try_compile "$LINENO"
@@ -3977,35 +4033,36 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
done
rm -f conftest.$ac_ext
CXX=$ac_save_CXX
fi
if test "x$ac_cv_prog_cxx_cxx11" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
else $as_nop
if test "x$ac_cv_prog_cxx_cxx11" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5
printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; }
CXX="$CXX $ac_cv_prog_cxx_cxx11"
fi
ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11
# AC_CACHE_VAL
ac_prog_cxx_stdcxx_options=
case "x$ac_cv_prog_cxx_cxx11" in #(
x) :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; } ;; #(
xno) :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; } ;; #(
*) :
ac_prog_cxx_stdcxx_options=" $ac_cv_prog_cxx_cxx11"
CXX=$CXX$ac_prog_cxx_stdcxx_options
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx11" >&5
printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; } ;;
esac
if test "x$ac_cv_prog_cxx_cxx11" != xno
then :
ac_prog_cxx_stdcxx=cxx11
fi
fi
if test x$ac_prog_cxx_stdcxx = xno
then :
ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11
ac_cv_prog_cxx_cxx98=$ac_cv_prog_cxx_cxx11
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5
printf %s "checking for $CXX option to enable C++98 features... " >&6; }
if test ${ac_cv_prog_cxx_98+y}
if test ${ac_cv_prog_cxx_cxx98+y}
then :
printf %s "(cached) " >&6
else $as_nop
ac_cv_prog_cxx_98=no
ac_cv_prog_cxx_cxx98=no
ac_save_CXX=$CXX
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -4023,25 +4080,32 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
done
rm -f conftest.$ac_ext
CXX=$ac_save_CXX
fi
# AC_CACHE_VAL
ac_prog_cxx_stdcxx_options=
case "x$ac_cv_prog_cxx_cxx98" in #(
x) :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; } ;; #(
xno) :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; } ;; #(
*) :
ac_prog_cxx_stdcxx_options=" $ac_cv_prog_cxx_cxx98"
CXX=$CXX$ac_prog_cxx_stdcxx_options
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5
printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; } ;;
esac
if test "x$ac_cv_prog_cxx_cxx98" != xno
then :
ac_prog_cxx_stdcxx=cxx98
ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98
else $as_nop
ac_prog_cxx_stdcxx=no
ac_cv_prog_cxx_stdcxx=no
fi
if test "x$ac_cv_prog_cxx_cxx98" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
else $as_nop
if test "x$ac_cv_prog_cxx_cxx98" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_cxx98" >&5
printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; }
CXX="$CXX $ac_cv_prog_cxx_cxx98"
fi
ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98
ac_prog_cxx_stdcxx=cxx98
fi
fi
ac_ext=c
@@ -4386,7 +4450,12 @@ program_transform_name=`printf "%s\n" "$program_transform_name" | sed "$ac_scrip
if test x"${MISSING+set}" != xset; then
MISSING="\${SHELL} '$am_aux_dir/missing'"
case $am_aux_dir in
*\ * | *\ *)
MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
*)
MISSING="\${SHELL} $am_aux_dir/missing" ;;
esac
fi
# Use eval to expand $SHELL
if eval "$MISSING --is-lightweight"; then
@@ -4779,7 +4848,7 @@ fi
# Define the identity of the package.
PACKAGE='qbittorrent'
VERSION='v4.4.0alpha'
VERSION='v4.3.5'
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -5456,8 +5525,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.11\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.11") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
@@ -5466,12 +5535,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.11\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.11") 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.11" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -5501,8 +5570,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.11" >&5
printf %s "checking for Qt5 qmake >= 5.11... " >&6; }
if test "x$QT_QMAKE" != "x"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $QT_QMAKE" >&5
@@ -5529,12 +5598,12 @@ 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.11\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Svg >= 5.11") 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.11" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -5546,12 +5615,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.11\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5Svg >= 5.11") 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.11" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -5572,14 +5641,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.11" 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.11" 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.11) were not met:
$Qt5Svg_PKG_ERRORS
@@ -5619,11 +5688,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.11" >&5
printf %s "checking for Qt5DBus >= 5.11... " >&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.11\""; } >&5
($PKG_CONFIG --exists --print-errors "Qt5DBus >= 5.11") 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
@@ -6031,6 +6100,252 @@ fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
# Check whether --with-boost-system was given.
if test ${with_boost_system+y}
then :
withval=$with_boost_system;
if test "$withval" = "no"; then
want_boost="no"
elif test "$withval" = "yes"; then
want_boost="yes"
ax_boost_user_system_lib=""
else
want_boost="yes"
ax_boost_user_system_lib="$withval"
fi
else $as_nop
want_boost="yes"
fi
if test "x$want_boost" = "xyes"; then
CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
export LDFLAGS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the Boost::System library is available" >&5
printf %s "checking whether the Boost::System library is available... " >&6; }
if test ${ax_cv_boost_system+y}
then :
printf %s "(cached) " >&6
else $as_nop
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
CXXFLAGS_SAVE=$CXXFLAGS
CXXFLAGS=
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <boost/system/error_code.hpp>
int
main (void)
{
boost::system::error_category *a = 0;
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"
then :
ax_cv_boost_system=yes
else $as_nop
ax_cv_boost_system=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
CXXFLAGS=$CXXFLAGS_SAVE
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_boost_system" >&5
printf "%s\n" "$ax_cv_boost_system" >&6; }
if test "x$ax_cv_boost_system" = "xyes"; then
printf "%s\n" "#define HAVE_BOOST_SYSTEM /**/" >>confdefs.h
BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/[^\/]*//'`
LDFLAGS_SAVE=$LDFLAGS
if test "x$ax_boost_user_system_lib" = "x"; then
for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
ax_lib=${libextension}
as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ax_lib""_exit" | $as_tr_sh`
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5
printf %s "checking for exit in -l$ax_lib... " >&6; }
if eval test \${$as_ac_Lib+y}
then :
printf %s "(cached) " >&6
else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-l$ax_lib $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
namespace conftest {
extern "C" int exit ();
}
int
main (void)
{
return conftest::exit ();
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"
then :
eval "$as_ac_Lib=yes"
else $as_nop
eval "$as_ac_Lib=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
eval ac_res=\$$as_ac_Lib
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
if eval test \"x\$"$as_ac_Lib"\" = x"yes"
then :
BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break
else $as_nop
link_system="no"
fi
done
if test "x$link_system" != "xyes"; then
for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do
ax_lib=${libextension}
as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ax_lib""_exit" | $as_tr_sh`
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5
printf %s "checking for exit in -l$ax_lib... " >&6; }
if eval test \${$as_ac_Lib+y}
then :
printf %s "(cached) " >&6
else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-l$ax_lib $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
namespace conftest {
extern "C" int exit ();
}
int
main (void)
{
return conftest::exit ();
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"
then :
eval "$as_ac_Lib=yes"
else $as_nop
eval "$as_ac_Lib=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
eval ac_res=\$$as_ac_Lib
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
if eval test \"x\$"$as_ac_Lib"\" = x"yes"
then :
BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break
else $as_nop
link_system="no"
fi
done
fi
else
for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do
as_ac_Lib=`printf "%s\n" "ac_cv_lib_$ax_lib""_exit" | $as_tr_sh`
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for exit in -l$ax_lib" >&5
printf %s "checking for exit in -l$ax_lib... " >&6; }
if eval test \${$as_ac_Lib+y}
then :
printf %s "(cached) " >&6
else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-l$ax_lib $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
namespace conftest {
extern "C" int exit ();
}
int
main (void)
{
return conftest::exit ();
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"
then :
eval "$as_ac_Lib=yes"
else $as_nop
eval "$as_ac_Lib=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
eval ac_res=\$$as_ac_Lib
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
if eval test \"x\$"$as_ac_Lib"\" = x"yes"
then :
BOOST_SYSTEM_LIB="-l$ax_lib"; link_system="yes"; break
else $as_nop
link_system="no"
fi
done
fi
if test "x$ax_lib" = "x"; then
as_fn_error $? "Could not find a version of the Boost::System library!" "$LINENO" 5
fi
if test "x$link_system" = "xno"; then
as_fn_error $? "Could not link against $ax_lib !" "$LINENO" 5
fi
fi
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: Boost.System LIB: \"$BOOST_SYSTEM_LIB\"" >&5
printf "%s\n" "$as_me: Boost.System LIB: \"$BOOST_SYSTEM_LIB\"" >&6;}
LIBS="$BOOST_SYSTEM_LIB $LIBS"
pkg_failed=no
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent" >&5
printf %s "checking for libtorrent... " >&6; }
@@ -6039,12 +6354,12 @@ 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 >= 1.2.12\""; } >&5
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.12") 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 >= 1.2.12" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -6056,12 +6371,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 >= 1.2.12\""; } >&5
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.2.12") 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 >= 1.2.12" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes
else
pkg_failed=yes
@@ -6082,14 +6397,14 @@ 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 >= 1.2.12" 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 >= 1.2.12" 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:
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.2.12) were not met:
$libtorrent_PKG_ERRORS
@@ -6396,7 +6711,7 @@ if ac_fn_cxx_try_compile "$LINENO"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
QBT_ADD_CONFIG="$QBT_ADD_CONFIG c++17"
QBT_ADD_CONFIG="$QBT_ADD_CONFIG c++1z"
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: C++17 mode is now force enabled. The C++ mode should match the mode that other libraries were built with, otherwise you'll likely get linking errors." >&5
printf "%s\n" "$as_me: WARNING: C++17 mode is now force enabled. The C++ mode should match the mode that other libraries were built with, otherwise you'll likely get linking errors." >&2;}
else $as_nop
@@ -7086,8 +7401,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v4.4.0alpha, which was
generated by GNU Autoconf 2.71. Invocation command line was
This file was extended by qbittorrent $as_me v4.3.5, which was
generated by GNU Autoconf 2.70. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -7146,11 +7461,11 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
qbittorrent config.status v4.4.0alpha
configured by $0, generated by GNU Autoconf 2.71,
qbittorrent config.status v4.3.5
configured by $0, generated by GNU Autoconf 2.70,
with options \\"\$ac_cs_config\\"
Copyright (C) 2021 Free Software Foundation, Inc.
Copyright (C) 2020 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."

View File

@@ -1,4 +1,4 @@
AC_INIT([qbittorrent], [v4.4.0alpha], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_INIT([qbittorrent], [v4.3.5], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
: ${CFLAGS=""}
@@ -141,7 +141,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.11])
])
AC_MSG_CHECKING([whether QtDBus should be enabled])
AS_CASE(["x$enable_qt_dbus"],
@@ -175,8 +175,12 @@ m4_define([DETECT_BOOST_VERSION_PROGRAM],
AC_COMPILE_IFELSE([DETECT_BOOST_VERSION_PROGRAM(106000)], [],
[QBT_ADD_DEFINES="$QBT_ADD_DEFINES BOOST_NO_CXX11_RVALUE_REFERENCES"])
AX_BOOST_SYSTEM()
AC_MSG_NOTICE([Boost.System LIB: "$BOOST_SYSTEM_LIB"])
LIBS="$BOOST_SYSTEM_LIB $LIBS"
PKG_CHECK_MODULES(libtorrent,
[libtorrent-rasterbar >= 1.2.13],
[libtorrent-rasterbar >= 1.2.12],
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS"
LIBS="$libtorrent_LIBS $LIBS"])
@@ -212,7 +216,7 @@ AS_IF([test "x$QBT_CXX17_FOUND" = "xno"],
CXXFLAGS="-std=c++17 $TMP_CXXFLAGS"
AC_COMPILE_IFELSE([DETECT_CPP17_PROGRAM()],
[AC_MSG_RESULT([no])
QBT_ADD_CONFIG="$QBT_ADD_CONFIG c++17"
QBT_ADD_CONFIG="$QBT_ADD_CONFIG c++1z"
AC_MSG_WARN([C++17 mode is now force enabled. The C++ mode should match the mode that other libraries were built with, otherwise you'll likely get linking errors.])],
[AC_MSG_RESULT([yes])
AC_MSG_ERROR([The compiler supports C++17 but the user or a dependency has explicitly enabled a lower mode.])])],

2
dist/mac/Info.plist vendored
View File

@@ -55,7 +55,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.4.0</string>
<string>4.3.5</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -30,6 +30,12 @@ install(FILES ${MAN_FILES}
)
if (GUI)
install(DIRECTORY menuicons/
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
COMPONENT data
FILES_MATCHING PATTERN "*.png"
)
install(FILES org.qbittorrent.qBittorrent.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
COMPONENT data
@@ -40,11 +46,6 @@ if (GUI)
COMPONENT data
)
install(DIRECTORY menuicons/
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
COMPONENT data
)
install(FILES
${PROJECT_SOURCE_DIR}/src/icons/qbittorrent-tray.svg
${PROJECT_SOURCE_DIR}/src/icons/qbittorrent-tray-dark.svg

View File

@@ -1,16 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
<title>
qbittorrent-new-light
</title>
<defs>
<linearGradient x1="34.012%" y1="0%" x2="76.373%" y2="76.805%" id="a">
<stop stop-color="#72B4F5" offset="0%"/>
<stop stop-color="#356EBF" offset="100%"/>
</linearGradient>
</defs>
<g fill="none" fill-rule="evenodd">
<circle stroke="#DAEFFF" stroke-width="32" fill="url(#a)" cx="512" cy="512" r="496"/>
<path d="M712.898 332.399q66.657 0 103.38 45.671 37.03 45.364 37.03 128.684t-37.34 129.61q-37.03 45.98-103.07 45.98-33.02 0-60.484-12.035-27.156-12.344-45.672-37.649h-3.703l-10.8 43.512h-36.724V196h51.227v116.65q0 39.191-2.469 70.359h2.47q35.796-50.61 106.155-50.61zm-7.406 42.894q-52.46 0-75.605 30.242-23.145 29.934-23.145 101.219t23.762 102.145q23.761 30.55 76.222 30.55 47.215 0 70.36-34.254 23.144-34.562 23.144-99.058 0-66.04-23.144-98.442-23.145-32.402-71.594-32.402z" fill="#fff"/>
<path d="M317.273 639.45q51.227 0 74.68-27.466 23.453-27.464 24.996-92.578v-11.418q0-70.976-24.07-102.144-24.07-31.168-76.223-31.168-45.055 0-69.125 35.18-23.762 34.87-23.762 98.75 0 63.879 23.454 97.515 23.761 33.328 70.05 33.328zm-7.715 42.894q-65.421 0-102.144-45.98-36.723-45.981-36.723-128.376 0-83.011 37.032-129.609 37.03-46.598 103.07-46.598 69.433 0 106.773 52.461h2.778l7.406-46.289h40.426V828h-51.227V683.27q0-30.86 3.395-52.461h-4.012q-35.488 51.535-106.774 51.535z" fill="#c8e8ff"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -74,6 +74,6 @@
<url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url>
<content_rating type="oars-1.1"/>
<releases>
<release version="4.4.0" date="2020-10-18"/>
<release version="4.3.5" date="2021-05-02"/>
</releases>
</component>

View File

@@ -142,8 +142,8 @@ Name[pt_BR]=qBittorrent
Comment[ro]=Descărcați și partajați fișiere prin BitTorrent
GenericName[ro]=Client BitTorrent
Name[ro]=qBittorrent
Comment[ru]=Обмен файлами по сети БитТоррент
GenericName[ru]=Клиент сети БитТоррент
Comment[ru]=Обмен файлами по сети BitTorrent
GenericName[ru]=Клиент сети BitTorrent
Name[ru]=qBittorrent
Comment[sk]=Sťahovanie a zdieľanie súborov prostredníctvom siete BitTorrent
GenericName[sk]=Klient siete BitTorrent
@@ -177,8 +177,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ẻ các tập tin thông qua BitTorrent
GenericName[vi]=Máy trạm dạng BitTorrent
Name[vi]=qBittorrent
Comment[az@latin]=Faylları BitTorrent vasitəsilə göndərin və paylaşın
GenericName[az@latin]=BitTorrent client

View File

@@ -1,60 +1,60 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_HUNGARIAN} "qBittorrent (kötelező)"
LangString inst_qbt_req ${LANG_HUNGARIAN} "qBittorrent (required)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_HUNGARIAN} "Asztali parancsikon létrehozása"
LangString inst_dekstop ${LANG_HUNGARIAN} "Create Desktop Shortcut"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_HUNGARIAN} "Start menüben parancsikon létrehozása"
LangString inst_startmenu ${LANG_HUNGARIAN} "Create Start Menu Shortcut"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_HUNGARIAN} "qBittorrent indítása Windows indításakor"
LangString inst_startup ${LANG_HUNGARIAN} "Start qBittorrent on Windows start up"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_HUNGARIAN} ".torrent fájlok megnyitása qBittorrenttel"
LangString inst_torrent ${LANG_HUNGARIAN} "Open .torrent files with qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_HUNGARIAN} "Magnet linkek megnyitása qBittorrenttel"
LangString inst_magnet ${LANG_HUNGARIAN} "Open magnet links with qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_HUNGARIAN} "Windows Tűzfal szabály hozzáadása"
LangString inst_firewall ${LANG_HUNGARIAN} "Add Windows Firewall rule"
;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_HUNGARIAN} "A Windows elérési útvonalak hosszának feloldása (A MAX_PATH korlátozás 260 karakteres, Windows 10 1607 vagy újabb verzió szükséges)"
LangString inst_pathlimit ${LANG_HUNGARIAN} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_HUNGARIAN} "Windows Tűzfal szabály hozzáadása folyamatban"
LangString inst_firewallinfo ${LANG_HUNGARIAN} "Adding Windows Firewall rule"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_HUNGARIAN} "A qBittorrent fut. Kérem zárjba be az alkalmazást a telepítés előtt."
LangString inst_warning ${LANG_HUNGARIAN} "qBittorrent is running. Please close the application before installing."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_HUNGARIAN} "A jelenlegi verzió el lesz távolítva. A felhasználói beállítások és a torrentek megmaradnak."
LangString inst_uninstall_question ${LANG_HUNGARIAN} "Current version will be uninstalled. User settings and torrents will remain intact."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_HUNGARIAN} "Előző verzió eltávolítása."
LangString inst_unist ${LANG_HUNGARIAN} "Uninstalling previous version."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_HUNGARIAN} "qBittorrent indítása."
LangString launch_qbt ${LANG_HUNGARIAN} "Launch qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_HUNGARIAN} "A telepítő csak 64-bites Windows verziókon működik."
LangString inst_requires_64bit ${LANG_HUNGARIAN} "This installer works only in 64-bit Windows versions."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_HUNGARIAN} "A qBittorrent ezen verziójához minimum Windows 7 szükséges."
LangString inst_requires_win7 ${LANG_HUNGARIAN} "This qBittorrent version requires at least Windows 7."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_HUNGARIAN} "qBittorrent eltávolítása"
LangString inst_uninstall_link_description ${LANG_HUNGARIAN} "Uninstall qBittorrent"
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_HUNGARIAN} "Fájlok eltávolítása"
LangString remove_files ${LANG_HUNGARIAN} "Remove files"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_HUNGARIAN} "Parancsikonok eltávolítása"
LangString remove_shortcuts ${LANG_HUNGARIAN} "Remove shortcuts"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_HUNGARIAN} "Fájltársítások eltávolítása"
LangString remove_associations ${LANG_HUNGARIAN} "Remove file associations"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_HUNGARIAN} "Regisztrációs kulcsok eltávolítása"
LangString remove_registry ${LANG_HUNGARIAN} "Remove registry keys"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_HUNGARIAN} "Konfigurációs fájlok eltávolítása"
LangString remove_conf ${LANG_HUNGARIAN} "Remove configuration files"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_HUNGARIAN} "Windows Tűzfal szabály eltávolítása"
LangString remove_firewall ${LANG_HUNGARIAN} "Remove Windows Firewall rule"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_HUNGARIAN} "Windows Tűzfal szabály eltávolítása folyamatban"
LangString remove_firewallinfo ${LANG_HUNGARIAN} "Removing Windows Firewall rule"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_HUNGARIAN} "Torrentek és gyorsítótárazott adatok eltávolítása"
LangString remove_cache ${LANG_HUNGARIAN} "Remove torrents and cached data"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_HUNGARIAN} "A qBittorrent jelenleg is fut. Kérem zárja be az alkalmazást mielőtt eltávolítaná."
LangString uninst_warning ${LANG_HUNGARIAN} "qBittorrent is running. Please close the application before uninstalling."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_HUNGARIAN} "A .torrent fájlok társítása nem lett eltávolítva. Társítva van:"
LangString uninst_tor_warn ${LANG_HUNGARIAN} "Not removing .torrent association. It is associated with:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_HUNGARIAN} "A magnet linkek társítása nem lett eltávolítva. Társítva van:"
LangString uninst_mag_warn ${LANG_HUNGARIAN} "Not removing magnet association. It is associated with:"

View File

@@ -19,9 +19,9 @@ LangString inst_pathlimit ${LANG_JAPANESE} "Windows のパスの文字長制限
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_JAPANESE} "Windows ファイアウォールのルールを追加"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_JAPANESE} "qBittorrent 実行中です。インストールの前にアプリケーションを終了してください。"
LangString inst_warning ${LANG_JAPANESE} "qBittorrent 実行中です。インストールの前にアプリケーションを終了してください。"
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_JAPANESE} "利用中のバージョンをアンインストールします。ユーザー設定やトレントは残されます。"
LangString inst_uninstall_question ${LANG_JAPANESE} "以前のインストールが検出されました。ユーザー設定を削除せずにアンインストールします。"
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_JAPANESE} "以前のバージョンをアンインストールしています。"
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
@@ -31,7 +31,7 @@ LangString inst_requires_64bit ${LANG_JAPANESE} "このインストーラーは
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_JAPANESE} "このバージョンの qBittorrent には Windows 7 以降が必要です。"
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_JAPANESE} "qBittorrent をアンインストール"
LangString inst_uninstall_link_description ${LANG_JAPANESE} "Uninstall qBittorrent"
;------------------------------------
;Uninstaller strings

View File

@@ -1,60 +1,60 @@
;Installer strings
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
LangString inst_qbt_req ${LANG_SWEDISH} "qBittorrent (krävs)"
LangString inst_qbt_req ${LANG_SWEDISH} "qBittorrent (required)"
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
LangString inst_dekstop ${LANG_SWEDISH} "Skapa skrivbordsgenväg"
LangString inst_dekstop ${LANG_SWEDISH} "Create Desktop Shortcut"
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
LangString inst_startmenu ${LANG_SWEDISH} "Skapa startmenygenväg"
LangString inst_startmenu ${LANG_SWEDISH} "Create Start Menu Shortcut"
;LangString inst_startup ${LANG_ENGLISH} "Start qBittorrent on Windows start up"
LangString inst_startup ${LANG_SWEDISH} "Starta qBittorrent vid Windows start"
LangString inst_startup ${LANG_SWEDISH} "Start qBittorrent on Windows start up"
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
LangString inst_torrent ${LANG_SWEDISH} "Öppna .torrent-filer med qBittorrent"
LangString inst_torrent ${LANG_SWEDISH} "Open .torrent files with qBittorrent"
;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent"
LangString inst_magnet ${LANG_SWEDISH} "Öppna magnetlänkar med qBittorrent"
LangString inst_magnet ${LANG_SWEDISH} "Open magnet links with qBittorrent"
;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule"
LangString inst_firewall ${LANG_SWEDISH} "Lägg till Windows-brandväggregel"
LangString inst_firewall ${LANG_SWEDISH} "Add Windows Firewall rule"
;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_SWEDISH} "Inaktivera gränsen för Windows-sökvägslängd (260 tecken MAX_PATH-begränsning, kräver Windows 10 1607 eller senare)"
LangString inst_pathlimit ${LANG_SWEDISH} "Disable Windows path length limit (260 character MAX_PATH limitation, requires Windows 10 1607 or later)"
;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule"
LangString inst_firewallinfo ${LANG_SWEDISH} "Lägger till Windows-brandväggregel"
LangString inst_firewallinfo ${LANG_SWEDISH} "Adding Windows Firewall rule"
;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing."
LangString inst_warning ${LANG_SWEDISH} "qBittorrent körs. Vänligen stäng programmet innan du installerar."
LangString inst_warning ${LANG_SWEDISH} "qBittorrent is running. Please close the application before installing."
;LangString inst_uninstall_question ${LANG_ENGLISH} "Current version will be uninstalled. User settings and torrents will remain intact."
LangString inst_uninstall_question ${LANG_SWEDISH} "Nuvarande version avinstalleras. Användarinställningar och torrenter kommer att förbli intakta."
LangString inst_uninstall_question ${LANG_SWEDISH} "Current version will be uninstalled. User settings and torrents will remain intact."
;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version."
LangString inst_unist ${LANG_SWEDISH} "Avinstallerar tidigare version."
LangString inst_unist ${LANG_SWEDISH} "Uninstalling previous version."
;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent."
LangString launch_qbt ${LANG_SWEDISH} "Kör qBittorrent."
LangString launch_qbt ${LANG_SWEDISH} "Launch qBittorrent."
;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions."
LangString inst_requires_64bit ${LANG_SWEDISH} "Installationsprogrammet fungerar endast i 64-bitars Windows-versioner."
LangString inst_requires_64bit ${LANG_SWEDISH} "This installer works only in 64-bit Windows versions."
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_SWEDISH} "Den här qBittorrent-versionen kräver minst Windows 7."
LangString inst_requires_win7 ${LANG_SWEDISH} "This qBittorrent version requires at least Windows 7."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_SWEDISH} "Avinstallera qBittorrent"
LangString inst_uninstall_link_description ${LANG_SWEDISH} "Uninstall qBittorrent"
;------------------------------------
;Uninstaller strings
;LangString remove_files ${LANG_ENGLISH} "Remove files"
LangString remove_files ${LANG_SWEDISH} "Ta bort filer"
LangString remove_files ${LANG_SWEDISH} "Remove files"
;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts"
LangString remove_shortcuts ${LANG_SWEDISH} "Ta bort genvägar"
LangString remove_shortcuts ${LANG_SWEDISH} "Remove shortcuts"
;LangString remove_associations ${LANG_ENGLISH} "Remove file associations"
LangString remove_associations ${LANG_SWEDISH} "Ta bort filassociationer"
LangString remove_associations ${LANG_SWEDISH} "Remove file associations"
;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys"
LangString remove_registry ${LANG_SWEDISH} "Ta bort registernycklar"
LangString remove_registry ${LANG_SWEDISH} "Remove registry keys"
;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files"
LangString remove_conf ${LANG_SWEDISH} "Ta bort konfigurationsfiler"
LangString remove_conf ${LANG_SWEDISH} "Remove configuration files"
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
LangString remove_firewall ${LANG_SWEDISH} "Ta bort Windows-brandväggsregel"
LangString remove_firewall ${LANG_SWEDISH} "Remove Windows Firewall rule"
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
LangString remove_firewallinfo ${LANG_SWEDISH} "Tar bort Windows-brandväggsregel"
LangString remove_firewallinfo ${LANG_SWEDISH} "Removing Windows Firewall rule"
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
LangString remove_cache ${LANG_SWEDISH} "Ta bort torrenter och cachade data"
LangString remove_cache ${LANG_SWEDISH} "Remove torrents and cached data"
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
LangString uninst_warning ${LANG_SWEDISH} "qBittorrent körs. Vänligen stäng programmet innan du avinstallerar."
LangString uninst_warning ${LANG_SWEDISH} "qBittorrent is running. Please close the application before uninstalling."
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
LangString uninst_tor_warn ${LANG_SWEDISH} "Tar inte bort .torrent-association. Den är associerad med:"
LangString uninst_tor_warn ${LANG_SWEDISH} "Not removing .torrent association. It is associated with:"
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
LangString uninst_mag_warn ${LANG_SWEDISH} "Tar inte bort magnet-association. Den är associerad med:"
LangString uninst_mag_warn ${LANG_SWEDISH} "Not removing magnet association. It is associated with:"

View File

@@ -31,7 +31,7 @@ LangString inst_requires_64bit ${LANG_TURKISH} "Bu yükleyici sadece 64-bit Wind
;LangString inst_requires_win7 ${LANG_ENGLISH} "This qBittorrent version requires at least Windows 7."
LangString inst_requires_win7 ${LANG_TURKISH} "Bu qBittorrent sürümü en az Windows 7 gerektirir."
;LangString inst_uninstall_link_description ${LANG_ENGLISH} "Uninstall qBittorrent"
LangString inst_uninstall_link_description ${LANG_TURKISH} "qBittorrent'i kaldır"
LangString inst_uninstall_link_description ${LANG_TURKISH} "Uninstall qBittorrent"
;------------------------------------
;Uninstaller strings

View File

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

121
m4/ax_boost_system.m4 Normal file
View File

@@ -0,0 +1,121 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_boost_system.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_BOOST_SYSTEM
#
# DESCRIPTION
#
# Test for System library from the Boost C++ libraries. The macro requires
# a preceding call to AX_BOOST_BASE. Further documentation is available at
# <http://randspringer.de/boost/index.html>.
#
# This macro calls:
#
# AC_SUBST(BOOST_SYSTEM_LIB)
#
# And sets:
#
# HAVE_BOOST_SYSTEM
#
# LICENSE
#
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
# Copyright (c) 2008 Michael Tindal
# Copyright (c) 2008 Daniel Casimiro <dan.casimiro@gmail.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 20
AC_DEFUN([AX_BOOST_SYSTEM],
[
AC_ARG_WITH([boost-system],
AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@],
[use the System library from boost - it is possible to specify a certain library for the linker
e.g. --with-boost-system=boost_system-gcc-mt ]),
[
if test "$withval" = "no"; then
want_boost="no"
elif test "$withval" = "yes"; then
want_boost="yes"
ax_boost_user_system_lib=""
else
want_boost="yes"
ax_boost_user_system_lib="$withval"
fi
],
[want_boost="yes"]
)
if test "x$want_boost" = "xyes"; then
AC_REQUIRE([AC_PROG_CC])
AC_REQUIRE([AC_CANONICAL_BUILD])
CPPFLAGS_SAVED="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
export CPPFLAGS
LDFLAGS_SAVED="$LDFLAGS"
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
export LDFLAGS
AC_CACHE_CHECK(whether the Boost::System library is available,
ax_cv_boost_system,
[AC_LANG_PUSH([C++])
CXXFLAGS_SAVE=$CXXFLAGS
CXXFLAGS=
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/system/error_code.hpp>]],
[[boost::system::error_category *a = 0;]])],
ax_cv_boost_system=yes, ax_cv_boost_system=no)
CXXFLAGS=$CXXFLAGS_SAVE
AC_LANG_POP([C++])
])
if test "x$ax_cv_boost_system" = "xyes"; then
AC_SUBST(BOOST_CPPFLAGS)
AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available])
BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
LDFLAGS_SAVE=$LDFLAGS
if test "x$ax_boost_user_system_lib" = "x"; then
for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do
ax_lib=${libextension}
AC_CHECK_LIB($ax_lib, exit,
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
[link_system="no"])
done
if test "x$link_system" != "xyes"; then
for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do
ax_lib=${libextension}
AC_CHECK_LIB($ax_lib, exit,
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
[link_system="no"])
done
fi
else
for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do
AC_CHECK_LIB($ax_lib, exit,
[BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break],
[link_system="no"])
done
fi
if test "x$ax_lib" = "x"; then
AC_MSG_ERROR(Could not find a version of the Boost::System library!)
fi
if test "x$link_system" = "xno"; then
AC_MSG_ERROR(Could not link against $ax_lib !)
fi
fi
CPPFLAGS="$CPPFLAGS_SAVED"
LDFLAGS="$LDFLAGS_SAVED"
fi
])

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.11],
[PKG_CHECK_VAR(QT_QMAKE,
[Qt5Core >= 5.14],
[Qt5Core >= 5.11],
[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.11])
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.11])
PKG_CHECK_EXISTS([Qt5DBus >= 5.11],
[AC_MSG_RESULT([found])
HAVE_QTDBUS=[true]],
[AC_MSG_RESULT([not found])

View File

@@ -35,7 +35,7 @@ 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)
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS Core Network Xml LinguistTools)
if (DBUS)
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS DBus)
set_package_properties(Qt5DBus PROPERTIES
@@ -61,7 +61,9 @@ add_subdirectory(base)
if (GUI)
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS Widgets Svg)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS MacExtras)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
find_package(Qt5 ${minQtVersion} REQUIRED COMPONENTS WinExtras)
endif()
add_subdirectory(gui)

View File

@@ -4,14 +4,22 @@
# 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)
if (Qt5_VERSION VERSION_LESS 5.12)
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
else()
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES} OPTIONS -silent)
endif()
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)
if (Qt5_VERSION VERSION_LESS 5.12)
qt5_add_translation(QBT_WEBUI_QM_FILES ${QBT_WEBUI_TS_FILES})
else()
qt5_add_translation(QBT_WEBUI_QM_FILES ${QBT_WEBUI_TS_FILES} OPTIONS -silent)
endif()
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 +147,11 @@ 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)
if (Qt5_VERSION VERSION_LESS 5.14)
set_property(TARGET qbt_app APPEND PROPERTY QT_PLUGINS Qt5::QSvgIconPlugin Qt5::QSvgPlugin)
else()
qt_import_plugins(qbt_app INCLUDE Qt5::QSvgIconPlugin Qt5::QSvgPlugin)
endif()
endif()
endif()

View File

@@ -75,12 +75,12 @@
#include "base/profile.h"
#include "base/rss/rss_autodownloader.h"
#include "base/rss/rss_session.h"
#include "base/scanfoldersmodel.h"
#include "base/search/searchpluginmanager.h"
#include "base/settingsstorage.h"
#include "base/torrentfileswatcher.h"
#include "base/utils/compare.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "base/version.h"
#include "applicationinstancemanager.h"
#include "filelogger.h"
@@ -302,7 +302,7 @@ void Application::setFileLoggerAgeType(const int value)
void Application::processMessage(const QString &message)
{
const QStringList params = message.split(PARAMS_SEPARATOR, Qt::SkipEmptyParts);
const QStringList params = message.split(PARAMS_SEPARATOR, QString::SkipEmptyParts);
// If Application is not running (i.e., other
// components are not ready) store params
if (m_running)
@@ -350,15 +350,13 @@ void Application::runExternalProgram(const BitTorrent::Torrent *torrent) const
#endif
break;
case u'G':
program.replace(i, 2, torrent->tags().join(QLatin1String(",")));
{
QStringList tags = torrent->tags().values();
std::sort(tags.begin(), tags.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
program.replace(i, 2, tags.join(','));
}
break;
case u'I':
program.replace(i, 2, (torrent->infoHash().v1().isValid() ? torrent->infoHash().v1().toString() : QLatin1String("-")));
break;
case u'J':
program.replace(i, 2, (torrent->infoHash().v2().isValid() ? torrent->infoHash().v2().toString() : QLatin1String("-")));
break;
case u'K':
program.replace(i, 2, torrent->id().toString());
break;
case u'L':
@@ -627,7 +625,7 @@ int Application::exec(const QStringList &params)
connect(BitTorrent::Session::instance(), &BitTorrent::Session::allTorrentsFinished, this, &Application::allTorrentsFinished, Qt::QueuedConnection);
Net::GeoIPManager::initInstance();
TorrentFilesWatcher::initInstance();
ScanFoldersModel::initInstance();
#ifndef DISABLE_WEBUI
m_webui = new WebUI;
@@ -644,7 +642,7 @@ int Application::exec(const QStringList &params)
catch (const RuntimeError &err)
{
#ifdef DISABLE_GUI
fprintf(stderr, "%s", qPrintable(err.message()));
fprintf(stderr, "%s", err.what());
#else
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Critical);
@@ -823,7 +821,7 @@ void Application::cleanup()
delete RSS::AutoDownloader::instance();
delete RSS::Session::instance();
TorrentFilesWatcher::freeInstance();
ScanFoldersModel::freeInstance();
BitTorrent::Session::freeInstance();
Net::GeoIPManager::freeInstance();
Net::DownloadManager::freeInstance();

View File

@@ -498,6 +498,17 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
return result;
}
CommandLineParameterError::CommandLineParameterError(const QString &messageForUser)
: std::runtime_error(messageForUser.toLocal8Bit().data())
, m_messageForUser(messageForUser)
{
}
const QString &CommandLineParameterError::messageForUser() const
{
return m_messageForUser;
}
QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN, int wrapAtColumn = WRAP_AT_COLUMN)
{
QStringList words = text.split(' ');

View File

@@ -31,12 +31,11 @@
#pragma once
#include <optional>
#include <stdexcept>
#include <QString>
#include <QStringList>
#include "base/exceptions.h"
class QProcessEnvironment;
struct QBtCommandLineParameters
@@ -68,10 +67,14 @@ struct QBtCommandLineParameters
QStringList paramList() const;
};
class CommandLineParameterError : public RuntimeError
class CommandLineParameterError : public std::runtime_error
{
public:
using RuntimeError::RuntimeError;
explicit CommandLineParameterError(const QString &messageForUser);
const QString &messageForUser() const;
private:
const QString m_messageForUser;
};
QBtCommandLineParameters parseCommandLine(const QStringList &args);

View File

@@ -126,9 +126,7 @@ void FileLogger::addLogMessage(const Log::Msg &msg)
if (!m_logFile.isOpen()) return;
QTextStream stream(&m_logFile);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
stream.setCodec("UTF-8");
#endif
switch (msg.type)
{

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 !defined(DISABLE_GUI) && (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// 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);
@@ -235,7 +235,7 @@ int main(int argc, char *argv[])
// 3. https://bugreports.qt.io/browse/QTBUG-46015
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && !defined(DISABLE_GUI)
#if !defined(DISABLE_GUI)
// this is the default in Qt6
app->setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif
@@ -311,7 +311,7 @@ int main(int argc, char *argv[])
}
catch (const CommandLineParameterError &er)
{
displayBadArgMessage(er.message());
displayBadArgMessage(er.messageForUser());
return EXIT_FAILURE;
}
}

View File

@@ -79,7 +79,6 @@
#include <QDir>
#include <QLocalServer>
#include <QLocalSocket>
#include <QRegularExpression>
#include "base/utils/misc.h"
@@ -96,7 +95,7 @@ namespace QtLP_Private
const char* QtLocalPeer::ack = "ack";
QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
: QObject(parent)
, id(appId)
{
@@ -109,7 +108,7 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
#endif
prefix = id.section(QLatin1Char('/'), -1);
}
prefix.remove(QRegularExpression("[^a-zA-Z]"));
prefix.remove(QRegExp("[^a-zA-Z]"));
prefix.truncate(6);
QByteArray idc = id.toUtf8();
@@ -134,15 +133,6 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId)
lockFile.open(QIODevice::ReadWrite);
}
QtLocalPeer::~QtLocalPeer()
{
if (!isClient())
{
lockFile.unlock();
lockFile.remove();
}
}
bool QtLocalPeer::isClient()
{
if (lockFile.isLocked())

View File

@@ -78,7 +78,6 @@ class QtLocalPeer : public QObject
public:
QtLocalPeer(QObject *parent = nullptr, const QString &appId = QString());
~QtLocalPeer() override;
bool isClient();
bool sendMessage(const QString &message, int timeout);

View File

@@ -5,16 +5,13 @@ add_library(qbt_base STATIC
bittorrent/abstractfilestorage.h
bittorrent/addtorrentparams.h
bittorrent/bandwidthscheduler.h
bittorrent/bencoderesumedatastorage.h
bittorrent/cachestatus.h
bittorrent/common.h
bittorrent/customstorage.h
bittorrent/dbresumedatastorage.h
bittorrent/downloadpriority.h
bittorrent/filesearcher.h
bittorrent/filterparserthread.h
bittorrent/infohash.h
bittorrent/loadtorrentparams.h
bittorrent/ltqhash.h
bittorrent/ltunderlyingtype.h
bittorrent/magneturi.h
@@ -23,7 +20,7 @@ add_library(qbt_base STATIC
bittorrent/peeraddress.h
bittorrent/peerinfo.h
bittorrent/portforwarderimpl.h
bittorrent/resumedatastorage.h
bittorrent/resumedatasavingmanager.h
bittorrent/session.h
bittorrent/sessionstatus.h
bittorrent/speedmonitor.h
@@ -37,6 +34,7 @@ add_library(qbt_base STATIC
bittorrent/trackerentry.h
digest32.h
exceptions.h
filesystemwatcher.h
global.h
http/connection.h
http/httperror.h
@@ -58,7 +56,6 @@ add_library(qbt_base STATIC
net/proxyconfigurationmanager.h
net/reverseresolution.h
net/smtp.h
orderedset.h
preferences.h
profile.h
profile_p.h
@@ -70,18 +67,16 @@ add_library(qbt_base STATIC
rss/rss_item.h
rss/rss_parser.h
rss/rss_session.h
scanfoldersmodel.h
search/searchdownloadhandler.h
search/searchhandler.h
search/searchpluginmanager.h
settingsstorage.h
tagset.h
torrentfileguard.h
torrentfileswatcher.h
torrentfilter.h
types.h
unicodestrings.h
utils/bytearray.h
utils/compare.h
utils/foreignapps.h
utils/fs.h
utils/gzip.h
@@ -98,9 +93,7 @@ add_library(qbt_base STATIC
asyncfilestorage.cpp
bittorrent/abstractfilestorage.cpp
bittorrent/bandwidthscheduler.cpp
bittorrent/bencoderesumedatastorage.cpp
bittorrent/customstorage.cpp
bittorrent/dbresumedatastorage.cpp
bittorrent/downloadpriority.cpp
bittorrent/filesearcher.cpp
bittorrent/filterparserthread.cpp
@@ -111,6 +104,7 @@ add_library(qbt_base STATIC
bittorrent/peeraddress.cpp
bittorrent/peerinfo.cpp
bittorrent/portforwarderimpl.cpp
bittorrent/resumedatasavingmanager.cpp
bittorrent/session.cpp
bittorrent/speedmonitor.cpp
bittorrent/statistics.cpp
@@ -121,6 +115,7 @@ add_library(qbt_base STATIC
bittorrent/tracker.cpp
bittorrent/trackerentry.cpp
exceptions.cpp
filesystemwatcher.cpp
http/connection.cpp
http/httperror.cpp
http/requestparser.cpp
@@ -149,16 +144,14 @@ add_library(qbt_base STATIC
rss/rss_item.cpp
rss/rss_parser.cpp
rss/rss_session.cpp
scanfoldersmodel.cpp
search/searchdownloadhandler.cpp
search/searchhandler.cpp
search/searchpluginmanager.cpp
settingsstorage.cpp
tagset.cpp
torrentfileguard.cpp
torrentfileswatcher.cpp
torrentfilter.cpp
utils/bytearray.cpp
utils/compare.cpp
utils/foreignapps.cpp
utils/fs.cpp
utils/gzip.cpp
@@ -176,7 +169,7 @@ target_link_libraries(qbt_base
ZLIB::ZLIB
PUBLIC
LibtorrentRasterbar::torrent-rasterbar
Qt5::Core Qt5::Network Qt5::Sql Qt5::Xml
Qt5::Core Qt5::Network Qt5::Xml
qbt_common_cfg
)

View File

@@ -44,7 +44,7 @@ namespace Algorithm
{
};
// To be used with associative array types, such as QMap, QHash and its variants
// To be used with associative array types, such as QMap, QHash and it's variants
template <typename T, typename BinaryPredicate
, typename std::enable_if_t<HasMappedType<T>::value, int> = 0>
void removeIf(T &dict, BinaryPredicate &&p)

View File

@@ -4,16 +4,13 @@ HEADERS += \
$$PWD/bittorrent/abstractfilestorage.h \
$$PWD/bittorrent/addtorrentparams.h \
$$PWD/bittorrent/bandwidthscheduler.h \
$$PWD/bittorrent/bencoderesumedatastorage.h \
$$PWD/bittorrent/cachestatus.h \
$$PWD/bittorrent/common.h \
$$PWD/bittorrent/customstorage.h \
$$PWD/bittorrent/downloadpriority.h \
$$PWD/bittorrent/dbresumedatastorage.h \
$$PWD/bittorrent/filesearcher.h \
$$PWD/bittorrent/filterparserthread.h \
$$PWD/bittorrent/infohash.h \
$$PWD/bittorrent/loadtorrentparams.h \
$$PWD/bittorrent/ltqhash.h \
$$PWD/bittorrent/ltunderlyingtype.h \
$$PWD/bittorrent/magneturi.h \
@@ -22,7 +19,7 @@ HEADERS += \
$$PWD/bittorrent/peeraddress.h \
$$PWD/bittorrent/peerinfo.h \
$$PWD/bittorrent/portforwarderimpl.h \
$$PWD/bittorrent/resumedatastorage.h \
$$PWD/bittorrent/resumedatasavingmanager.h \
$$PWD/bittorrent/session.h \
$$PWD/bittorrent/sessionstatus.h \
$$PWD/bittorrent/speedmonitor.h \
@@ -36,6 +33,7 @@ HEADERS += \
$$PWD/bittorrent/trackerentry.h \
$$PWD/digest32.h \
$$PWD/exceptions.h \
$$PWD/filesystemwatcher.h \
$$PWD/global.h \
$$PWD/http/connection.h \
$$PWD/http/httperror.h \
@@ -57,7 +55,6 @@ HEADERS += \
$$PWD/net/proxyconfigurationmanager.h \
$$PWD/net/reverseresolution.h \
$$PWD/net/smtp.h \
$$PWD/orderedset.h \
$$PWD/preferences.h \
$$PWD/profile.h \
$$PWD/profile_p.h \
@@ -69,19 +66,17 @@ HEADERS += \
$$PWD/rss/rss_item.h \
$$PWD/rss/rss_parser.h \
$$PWD/rss/rss_session.h \
$$PWD/scanfoldersmodel.h \
$$PWD/search/searchdownloadhandler.h \
$$PWD/search/searchhandler.h \
$$PWD/search/searchpluginmanager.h \
$$PWD/settingsstorage.h \
$$PWD/settingvalue.h \
$$PWD/tagset.h \
$$PWD/torrentfileguard.h \
$$PWD/torrentfileswatcher.h \
$$PWD/torrentfilter.h \
$$PWD/types.h \
$$PWD/unicodestrings.h \
$$PWD/utils/bytearray.h \
$$PWD/utils/compare.h \
$$PWD/utils/foreignapps.h \
$$PWD/utils/fs.h \
$$PWD/utils/gzip.h \
@@ -98,9 +93,7 @@ SOURCES += \
$$PWD/asyncfilestorage.cpp \
$$PWD/bittorrent/abstractfilestorage.cpp \
$$PWD/bittorrent/bandwidthscheduler.cpp \
$$PWD/bittorrent/bencoderesumedatastorage.cpp \
$$PWD/bittorrent/customstorage.cpp \
$$PWD/bittorrent/dbresumedatastorage.cpp \
$$PWD/bittorrent/downloadpriority.cpp \
$$PWD/bittorrent/filesearcher.cpp \
$$PWD/bittorrent/filterparserthread.cpp \
@@ -111,6 +104,7 @@ SOURCES += \
$$PWD/bittorrent/peeraddress.cpp \
$$PWD/bittorrent/peerinfo.cpp \
$$PWD/bittorrent/portforwarderimpl.cpp \
$$PWD/bittorrent/resumedatasavingmanager.cpp \
$$PWD/bittorrent/session.cpp \
$$PWD/bittorrent/speedmonitor.cpp \
$$PWD/bittorrent/statistics.cpp \
@@ -121,6 +115,7 @@ SOURCES += \
$$PWD/bittorrent/tracker.cpp \
$$PWD/bittorrent/trackerentry.cpp \
$$PWD/exceptions.cpp \
$$PWD/filesystemwatcher.cpp \
$$PWD/http/connection.cpp \
$$PWD/http/httperror.cpp \
$$PWD/http/requestparser.cpp \
@@ -149,16 +144,14 @@ SOURCES += \
$$PWD/rss/rss_item.cpp \
$$PWD/rss/rss_parser.cpp \
$$PWD/rss/rss_session.cpp \
$$PWD/scanfoldersmodel.cpp \
$$PWD/search/searchdownloadhandler.cpp \
$$PWD/search/searchhandler.cpp \
$$PWD/search/searchpluginmanager.cpp \
$$PWD/settingsstorage.cpp \
$$PWD/tagset.cpp \
$$PWD/torrentfileguard.cpp \
$$PWD/torrentfileswatcher.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/utils/bytearray.cpp \
$$PWD/utils/compare.cpp \
$$PWD/utils/foreignapps.cpp \
$$PWD/utils/fs.cpp \
$$PWD/utils/gzip.cpp \

View File

@@ -40,8 +40,6 @@ namespace BitTorrent
Q_DECLARE_TR_FUNCTIONS(AbstractFileStorage)
public:
virtual ~AbstractFileStorage() = default;
virtual int filesCount() const = 0;
virtual QString filePath(int index) const = 0;
virtual QString fileName(int index) const = 0;

View File

@@ -30,11 +30,10 @@
#include <optional>
#include <QMetaType>
#include <QSet>
#include <QString>
#include <QVector>
#include "base/tagset.h"
#include "torrent.h"
#include "torrentcontentlayout.h"
@@ -46,7 +45,7 @@ namespace BitTorrent
{
QString name;
QString category;
TagSet tags;
QSet<QString> tags;
QString savePath;
bool disableTempPath = false; // e.g. for imported torrents
bool sequential = false;
@@ -63,5 +62,3 @@ namespace BitTorrent
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
};
}
Q_DECLARE_METATYPE(BitTorrent::AddTorrentParams)

View File

@@ -1,408 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "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/write_resume_data.hpp>
#include <QByteArray>
#include <QRegularExpression>
#include <QSaveFile>
#include <QThread>
#include "base/algorithm.h"
#include "base/exceptions.h"
#include "base/global.h"
#include "base/logger.h"
#include "base/profile.h"
#include "base/tagset.h"
#include "base/utils/fs.h"
#include "base/utils/io.h"
#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)
public:
explicit Worker(const QDir &resumeDataDir);
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
void remove(const TorrentID &id) const;
void storeQueue(const QVector<TorrentID> &queue) const;
private:
const QDir m_resumeDataDir;
};
}
namespace
{
template <typename LTStr>
QString fromLTString(const LTStr &str)
{
return QString::fromUtf8(str.data(), static_cast<int>(str.size()));
}
using ListType = lt::entry::list_type;
ListType setToEntryList(const TagSet &input)
{
ListType entryList;
entryList.reserve(input.size());
for (const QString &setValue : input)
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)
: ResumeDataStorage {parent}
, m_resumeDataDir {path}
, m_ioThread {new QThread {this}}
, m_asyncWorker {new Worker {m_resumeDataDir}}
{
if (!m_resumeDataDir.exists() && !m_resumeDataDir.mkpath(m_resumeDataDir.absolutePath()))
{
throw RuntimeError {tr("Cannot create torrent resume folder: \"%1\"")
.arg(Utils::Fs::toNativePath(m_resumeDataDir.absolutePath()))};
}
const QRegularExpression filenamePattern {QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$")};
const QStringList filenames = m_resumeDataDir.entryList(QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
m_registeredTorrents.reserve(filenames.size());
for (const QString &filename : filenames)
{
const QRegularExpressionMatch rxMatch = filenamePattern.match(filename);
if (rxMatch.hasMatch())
m_registeredTorrents.append(TorrentID::fromString(rxMatch.captured(1)));
}
loadQueue(m_resumeDataDir.absoluteFilePath(QLatin1String("queue")));
qDebug("Registered torrents count: %d", m_registeredTorrents.size());
m_asyncWorker->moveToThread(m_ioThread);
connect(m_ioThread, &QThread::finished, m_asyncWorker, &QObject::deleteLater);
m_ioThread->start();
}
BitTorrent::BencodeResumeDataStorage::~BencodeResumeDataStorage()
{
m_ioThread->quit();
m_ioThread->wait();
}
QVector<BitTorrent::TorrentID> BitTorrent::BencodeResumeDataStorage::registeredTorrents() const
{
return m_registeredTorrents;
}
std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorage::load(const TorrentID &id) const
{
const QString idString = id.toString();
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))
{
LogMsg(tr("Cannot read file %1: %2").arg(fastresumePath, file.errorString()), Log::WARNING);
return std::nullopt;
}
const QByteArray data = file.readAll();
const TorrentInfo metadata = TorrentInfo::loadFromFile(torrentFilePath);
return loadTorrentResumeData(data, metadata);
}
std::optional<BitTorrent::LoadTorrentParams> BitTorrent::BencodeResumeDataStorage::loadTorrentResumeData(
const QByteArray &data, const TorrentInfo &metadata) const
{
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;
LoadTorrentParams torrentParams;
torrentParams.restored = true;
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category"));
torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name"));
torrentParams.savePath = Profile::instance()->fromPortablePath(
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath"))));
torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus");
torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority");
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x.
// === BEGIN DEPRECATED CODE === //
const lt::bdecode_node contentLayoutNode = root.dict_find("qBt-contentLayout");
if (contentLayoutNode.type() == lt::bdecode_node::string_t)
{
const QString contentLayoutStr = fromLTString(contentLayoutNode.string_value());
torrentParams.contentLayout = Utils::String::toEnum(contentLayoutStr, TorrentContentLayout::Original);
}
else
{
const bool hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder");
torrentParams.contentLayout = (hasRootFolder ? TorrentContentLayout::Original : TorrentContentLayout::NoSubfolder);
}
// === END DEPRECATED CODE === //
// === BEGIN REPLACEMENT CODE === //
// torrentParams.contentLayout = Utils::String::parse(
// fromLTString(root.dict_find_string_value("qBt-contentLayout")), TorrentContentLayout::Default);
// === END REPLACEMENT CODE === //
const lt::string_view ratioLimitString = root.dict_find_string_value("qBt-ratioLimit");
if (ratioLimitString.empty())
torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", Torrent::USE_GLOBAL_RATIO * 1000) / 1000.0;
else
torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble();
const lt::bdecode_node tagsNode = root.dict_find("qBt-tags");
if (tagsNode.type() == lt::bdecode_node::list_t)
{
for (int i = 0; i < tagsNode.list_size(); ++i)
{
const QString tag = fromLTString(tagsNode.list_string_value_at(i));
torrentParams.tags.insert(tag);
}
}
lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
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)
{
// If torrent has "stop_when_ready" flag set then it is actually "stopped"
torrentParams.stopped = true;
torrentParams.operatingMode = TorrentOperatingMode::AutoManaged;
// ...but temporarily "resumed" to perform some service jobs (e.g. checking)
p.flags &= ~lt::torrent_flags::paused;
p.flags |= lt::torrent_flags::auto_managed;
}
else
{
torrentParams.stopped = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
torrentParams.operatingMode = (p.flags & lt::torrent_flags::paused) || (p.flags & lt::torrent_flags::auto_managed)
? TorrentOperatingMode::AutoManaged : TorrentOperatingMode::Forced;
}
const bool hasMetadata = (p.ti && p.ti->is_valid());
if (!hasMetadata && !root.dict_find("info-hash"))
return std::nullopt;
return torrentParams;
}
void BitTorrent::BencodeResumeDataStorage::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
{
QMetaObject::invokeMethod(m_asyncWorker, [this, id, resumeData]()
{
m_asyncWorker->store(id, resumeData);
});
}
void BitTorrent::BencodeResumeDataStorage::remove(const TorrentID &id) const
{
QMetaObject::invokeMethod(m_asyncWorker, [this, id]()
{
m_asyncWorker->remove(id);
});
}
void BitTorrent::BencodeResumeDataStorage::storeQueue(const QVector<TorrentID> &queue) const
{
QMetaObject::invokeMethod(m_asyncWorker, [this, queue]()
{
m_asyncWorker->storeQueue(queue);
});
}
void BitTorrent::BencodeResumeDataStorage::loadQueue(const QString &queueFilename)
{
QFile queueFile {queueFilename};
if (!queueFile.exists())
return;
if (queueFile.open(QFile::ReadOnly))
{
const QRegularExpression hashPattern {QLatin1String("^([A-Fa-f0-9]{40})$")};
QByteArray line;
int start = 0;
while (!(line = queueFile.readLine().trimmed()).isEmpty())
{
const QRegularExpressionMatch rxMatch = hashPattern.match(line);
if (rxMatch.hasMatch())
{
const auto torrentID = TorrentID::fromString(rxMatch.captured(1));
const int pos = m_registeredTorrents.indexOf(torrentID, start);
if (pos != -1)
{
std::swap(m_registeredTorrents[start], m_registeredTorrents[pos]);
++start;
}
}
}
}
else
{
LogMsg(tr("Couldn't load torrents queue from '%1'. Error: %2")
.arg(queueFile.fileName(), queueFile.errorString()), Log::WARNING);
}
}
BitTorrent::BencodeResumeDataStorage::Worker::Worker(const QDir &resumeDataDir)
: m_resumeDataDir {resumeDataDir}
{
}
void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
{
// We need to adjust native libtorrent resume data
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
if (resumeData.stopped)
{
p.flags |= lt::torrent_flags::paused;
p.flags &= ~lt::torrent_flags::auto_managed;
}
else
{
// Torrent can be actually "running" but temporarily "paused" to perform some
// service jobs behind the scenes so we need to restore it as "running"
if (resumeData.operatingMode == BitTorrent::TorrentOperatingMode::AutoManaged)
{
p.flags |= lt::torrent_flags::auto_managed;
}
else
{
p.flags &= ~lt::torrent_flags::paused;
p.flags &= ~lt::torrent_flags::auto_managed;
}
}
// metadata is stored in separate .torrent file
const std::shared_ptr<lt::torrent_info> torrentInfo = std::move(p.ti);
if (torrentInfo)
{
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)
{
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);
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;
data["qBt-category"] = resumeData.category.toStdString();
data["qBt-tags"] = setToEntryList(resumeData.tags);
data["qBt-name"] = resumeData.name.toStdString();
data["qBt-seedStatus"] = resumeData.hasSeedStatus;
data["qBt-contentLayout"] = Utils::String::fromEnum(resumeData.contentLayout).toStdString();
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)
{
LogMsg(tr("Couldn't save torrent resume data to '%1'. Error: %2.")
.arg(resumeFilepath, err.message()), Log::CRITICAL);
}
}
void BitTorrent::BencodeResumeDataStorage::Worker::remove(const TorrentID &id) const
{
const QString resumeFilename = QString::fromLatin1("%1.fastresume").arg(id.toString());
Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(resumeFilename));
const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(id.toString());
Utils::Fs::forceRemove(m_resumeDataDir.absoluteFilePath(torrentFilename));
}
void BitTorrent::BencodeResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue) const
{
QByteArray data;
data.reserve(((BitTorrent::TorrentID::length() * 2) + 1) * queue.size());
for (const BitTorrent::TorrentID &torrentID : queue)
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())
{
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::CRITICAL);
}
}

View File

@@ -1,69 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <QDir>
#include <QVector>
#include "resumedatastorage.h"
class QByteArray;
class QThread;
namespace BitTorrent
{
class TorrentInfo;
class BencodeResumeDataStorage final : public ResumeDataStorage
{
Q_OBJECT
Q_DISABLE_COPY(BencodeResumeDataStorage)
public:
explicit BencodeResumeDataStorage(const QString &path, QObject *parent = nullptr);
~BencodeResumeDataStorage() override;
QVector<TorrentID> registeredTorrents() const override;
std::optional<LoadTorrentParams> load(const TorrentID &id) const override;
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const override;
void remove(const TorrentID &id) const override;
void storeQueue(const QVector<TorrentID> &queue) const override;
private:
void loadQueue(const QString &queueFilename);
std::optional<LoadTorrentParams> loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata) const;
const QDir m_resumeDataDir;
QVector<TorrentID> m_registeredTorrents;
QThread *m_ioThread = nullptr;
class Worker;
Worker *m_asyncWorker = nullptr;
};
}

View File

@@ -1,586 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "dbresumedatastorage.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/write_resume_data.hpp>
#include <QByteArray>
#include <QFile>
#include <QSet>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QThread>
#include <QVector>
#include "base/exceptions.h"
#include "base/global.h"
#include "base/logger.h"
#include "base/profile.h"
#include "base/utils/fs.h"
#include "base/utils/string.h"
#include "infohash.h"
#include "loadtorrentparams.h"
#include "torrentinfo.h"
namespace
{
const char DB_CONNECTION_NAME[] = "ResumeDataStorage";
const int DB_VERSION = 1;
const char DB_TABLE_META[] = "meta";
const char DB_TABLE_TORRENTS[] = "torrents";
struct Column
{
QString name;
QString placeholder;
};
Column makeColumn(const char *columnName)
{
return {QLatin1String(columnName), (QLatin1Char(':') + QLatin1String(columnName))};
}
const Column DB_COLUMN_ID = makeColumn("id");
const Column DB_COLUMN_TORRENT_ID = makeColumn("torrent_id");
const Column DB_COLUMN_QUEUE_POSITION = makeColumn("queue_position");
const Column DB_COLUMN_NAME = makeColumn("name");
const Column DB_COLUMN_CATEGORY = makeColumn("category");
const Column DB_COLUMN_TAGS = makeColumn("tags");
const Column DB_COLUMN_TARGET_SAVE_PATH = makeColumn("target_save_path");
const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn("content_layout");
const Column DB_COLUMN_RATIO_LIMIT = makeColumn("ratio_limit");
const Column DB_COLUMN_SEEDING_TIME_LIMIT = makeColumn("seeding_time_limit");
const Column DB_COLUMN_HAS_OUTER_PIECES_PRIORITY = makeColumn("has_outer_pieces_priority");
const Column DB_COLUMN_HAS_SEED_STATUS = makeColumn("has_seed_status");
const Column DB_COLUMN_OPERATING_MODE = makeColumn("operating_mode");
const Column DB_COLUMN_STOPPED = makeColumn("stopped");
const Column DB_COLUMN_RESUMEDATA = makeColumn("libtorrent_resume_data");
const Column DB_COLUMN_METADATA = makeColumn("metadata");
const Column DB_COLUMN_VALUE = makeColumn("value");
template <typename LTStr>
QString fromLTString(const LTStr &str)
{
return QString::fromUtf8(str.data(), static_cast<int>(str.size()));
}
QString quoted(const QString &name)
{
const QLatin1Char quote {'`'};
return (quote + name + quote);
}
QString makeCreateTableStatement(const QString &tableName, const QStringList &items)
{
return QString::fromLatin1("CREATE TABLE %1 (%2)").arg(quoted(tableName), items.join(QLatin1Char(',')));
}
QString makeInsertStatement(const QString &tableName, const QVector<Column> &columns)
{
QStringList names;
names.reserve(columns.size());
QStringList values;
values.reserve(columns.size());
for (const Column &column : columns)
{
names.append(quoted(column.name));
values.append(column.placeholder);
}
const QString jointNames = names.join(QLatin1Char(','));
const QString jointValues = values.join(QLatin1Char(','));
return QString::fromLatin1("INSERT INTO %1 (%2) VALUES (%3)")
.arg(quoted(tableName), jointNames, jointValues);
}
QString makeOnConflictUpdateStatement(const Column &constraint, const QVector<Column> &columns)
{
QStringList names;
names.reserve(columns.size());
QStringList values;
values.reserve(columns.size());
for (const Column &column : columns)
{
names.append(quoted(column.name));
values.append(column.placeholder);
}
const QString jointNames = names.join(QLatin1Char(','));
const QString jointValues = values.join(QLatin1Char(','));
return QString::fromLatin1(" ON CONFLICT (%1) DO UPDATE SET (%2) = (%3)")
.arg(quoted(constraint.name), jointNames, jointValues);
}
QString makeColumnDefinition(const Column &column, const char *definition)
{
return QString::fromLatin1("%1 %2").arg(quoted(column.name), QLatin1String(definition));
}
}
namespace BitTorrent
{
class DBResumeDataStorage::Worker final : public QObject
{
Q_DISABLE_COPY(Worker)
public:
Worker(const QString &dbPath, const QString &dbConnectionName);
void openDatabase() const;
void closeDatabase() const;
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const;
void remove(const TorrentID &id) const;
void storeQueue(const QVector<TorrentID> &queue) const;
private:
const QString m_path;
const QString m_connectionName;
};
}
BitTorrent::DBResumeDataStorage::DBResumeDataStorage(const QString &dbPath, QObject *parent)
: ResumeDataStorage {parent}
, m_ioThread {new QThread(this)}
{
const bool needCreateDB = !QFile::exists(dbPath);
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), DB_CONNECTION_NAME);
db.setDatabaseName(dbPath);
if (!db.open())
throw RuntimeError(db.lastError().text());
if (needCreateDB)
createDB();
m_asyncWorker = new Worker(dbPath, QLatin1String("ResumeDataStorageWorker"));
m_asyncWorker->moveToThread(m_ioThread);
connect(m_ioThread, &QThread::finished, m_asyncWorker, &QObject::deleteLater);
m_ioThread->start();
RuntimeError *errPtr = nullptr;
QMetaObject::invokeMethod(m_asyncWorker, [this, &errPtr]()
{
try
{
m_asyncWorker->openDatabase();
}
catch (const RuntimeError &err)
{
errPtr = new RuntimeError(err);
}
}, Qt::BlockingQueuedConnection);
if (errPtr)
throw *errPtr;
}
BitTorrent::DBResumeDataStorage::~DBResumeDataStorage()
{
QMetaObject::invokeMethod(m_asyncWorker, &Worker::closeDatabase);
QSqlDatabase::removeDatabase(DB_CONNECTION_NAME);
m_ioThread->quit();
m_ioThread->wait();
}
QVector<BitTorrent::TorrentID> BitTorrent::DBResumeDataStorage::registeredTorrents() const
{
const auto selectTorrentIDStatement = QString::fromLatin1("SELECT %1 FROM %2 ORDER BY %3;")
.arg(quoted(DB_COLUMN_TORRENT_ID.name), quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name));
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
QSqlQuery query {db};
if (!query.exec(selectTorrentIDStatement))
throw RuntimeError(query.lastError().text());
QVector<TorrentID> registeredTorrents;
registeredTorrents.reserve(query.size());
while (query.next())
registeredTorrents.append(BitTorrent::TorrentID::fromString(query.value(0).toString()));
return registeredTorrents;
}
std::optional<BitTorrent::LoadTorrentParams> BitTorrent::DBResumeDataStorage::load(const TorrentID &id) const
{
const QString selectTorrentStatement =
QString(QLatin1String("SELECT * FROM %1 WHERE %2 = %3;"))
.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_TORRENT_ID.name), DB_COLUMN_TORRENT_ID.placeholder);
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
QSqlQuery query {db};
try
{
if (!query.prepare(selectTorrentStatement))
throw RuntimeError(query.lastError().text());
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, id.toString());
if (!query.exec())
throw RuntimeError(query.lastError().text());
if (!query.next())
throw RuntimeError(tr("Not found."));
}
catch (const RuntimeError &err)
{
LogMsg(tr("Couldn't load resume data of torrent '%1'. Error: %2")
.arg(id.toString(), err.message()), Log::CRITICAL);
return std::nullopt;
}
LoadTorrentParams resumeData;
resumeData.restored = true;
resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
if (!tagsData.isEmpty())
{
const QStringList tagList = tagsData.split(QLatin1Char(','));
resumeData.tags.insert(tagList.cbegin(), tagList.cend());
}
resumeData.savePath = Profile::instance()->fromPortablePath(
Utils::Fs::toUniformPath(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
resumeData.hasSeedStatus = query.value(DB_COLUMN_HAS_SEED_STATUS.name).toBool();
resumeData.firstLastPiecePriority = query.value(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.name).toBool();
resumeData.ratioLimit = query.value(DB_COLUMN_RATIO_LIMIT.name).toInt() / 1000.0;
resumeData.seedingTimeLimit = query.value(DB_COLUMN_SEEDING_TIME_LIMIT.name).toInt();
resumeData.contentLayout = Utils::String::toEnum<TorrentContentLayout>(
query.value(DB_COLUMN_CONTENT_LAYOUT.name).toString(), TorrentContentLayout::Original);
resumeData.operatingMode = Utils::String::toEnum<TorrentOperatingMode>(
query.value(DB_COLUMN_OPERATING_MODE.name).toString(), TorrentOperatingMode::AutoManaged);
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
const QByteArray bencodedResumeData = query.value(DB_COLUMN_RESUMEDATA.name).toByteArray();
lt::error_code ec;
const lt::bdecode_node root = lt::bdecode(bencodedResumeData, 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;
}
void BitTorrent::DBResumeDataStorage::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
{
QMetaObject::invokeMethod(m_asyncWorker, [this, id, resumeData]()
{
m_asyncWorker->store(id, resumeData);
});
}
void BitTorrent::DBResumeDataStorage::remove(const BitTorrent::TorrentID &id) const
{
QMetaObject::invokeMethod(m_asyncWorker, [this, id]()
{
m_asyncWorker->remove(id);
});
}
void BitTorrent::DBResumeDataStorage::storeQueue(const QVector<TorrentID> &queue) const
{
QMetaObject::invokeMethod(m_asyncWorker, [this, queue]()
{
m_asyncWorker->storeQueue(queue);
});
}
void BitTorrent::DBResumeDataStorage::createDB() const
{
auto db = QSqlDatabase::database(DB_CONNECTION_NAME);
if (!db.transaction())
throw RuntimeError(db.lastError().text());
QSqlQuery query {db};
try
{
const QStringList tableMetaItems = {
makeColumnDefinition(DB_COLUMN_ID, "INTEGER PRIMARY KEY"),
makeColumnDefinition(DB_COLUMN_NAME, "TEXT NOT NULL UNIQUE"),
makeColumnDefinition(DB_COLUMN_VALUE, "BLOB")
};
const QString createTableMetaQuery = makeCreateTableStatement(DB_TABLE_META, tableMetaItems);
if (!query.exec(createTableMetaQuery))
throw RuntimeError(query.lastError().text());
const QString insertMetaVersionQuery = makeInsertStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
if (!query.prepare(insertMetaVersionQuery))
throw RuntimeError(query.lastError().text());
query.bindValue(DB_COLUMN_NAME.placeholder, QString::fromLatin1("version"));
query.bindValue(DB_COLUMN_VALUE.placeholder, DB_VERSION);
if (!query.exec())
throw RuntimeError(query.lastError().text());
const QStringList tableTorrentsItems = {
makeColumnDefinition(DB_COLUMN_ID, "INTEGER PRIMARY KEY"),
makeColumnDefinition(DB_COLUMN_TORRENT_ID, "BLOB NOT NULL UNIQUE"),
makeColumnDefinition(DB_COLUMN_QUEUE_POSITION, "INTEGER NOT NULL DEFAULT -1"),
makeColumnDefinition(DB_COLUMN_NAME, "TEXT"),
makeColumnDefinition(DB_COLUMN_CATEGORY, "TEXT"),
makeColumnDefinition(DB_COLUMN_TAGS, "TEXT"),
makeColumnDefinition(DB_COLUMN_TARGET_SAVE_PATH, "TEXT"),
makeColumnDefinition(DB_COLUMN_CONTENT_LAYOUT, "TEXT NOT NULL"),
makeColumnDefinition(DB_COLUMN_RATIO_LIMIT, "INTEGER NOT NULL"),
makeColumnDefinition(DB_COLUMN_SEEDING_TIME_LIMIT, "INTEGER NOT NULL"),
makeColumnDefinition(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY, "INTEGER NOT NULL"),
makeColumnDefinition(DB_COLUMN_HAS_SEED_STATUS, "INTEGER NOT NULL"),
makeColumnDefinition(DB_COLUMN_OPERATING_MODE, "TEXT NOT NULL"),
makeColumnDefinition(DB_COLUMN_STOPPED, "INTEGER NOT NULL"),
makeColumnDefinition(DB_COLUMN_RESUMEDATA, "BLOB NOT NULL"),
makeColumnDefinition(DB_COLUMN_METADATA, "BLOB")
};
const QString createTableTorrentsQuery = makeCreateTableStatement(DB_TABLE_TORRENTS, tableTorrentsItems);
if (!query.exec(createTableTorrentsQuery))
throw RuntimeError(query.lastError().text());
if (!db.commit())
throw RuntimeError(db.lastError().text());
}
catch (const RuntimeError &)
{
db.rollback();
throw;
}
}
BitTorrent::DBResumeDataStorage::Worker::Worker(const QString &dbPath, const QString &dbConnectionName)
: m_path {dbPath}
, m_connectionName {dbConnectionName}
{
}
void BitTorrent::DBResumeDataStorage::Worker::openDatabase() const
{
auto db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), m_connectionName);
db.setDatabaseName(m_path);
if (!db.open())
throw RuntimeError(db.lastError().text());
}
void BitTorrent::DBResumeDataStorage::Worker::closeDatabase() const
{
QSqlDatabase::removeDatabase(m_connectionName);
}
void BitTorrent::DBResumeDataStorage::Worker::store(const TorrentID &id, const LoadTorrentParams &resumeData) const
{
// We need to adjust native libtorrent resume data
lt::add_torrent_params p = resumeData.ltAddTorrentParams;
p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
if (resumeData.stopped)
{
p.flags |= lt::torrent_flags::paused;
p.flags &= ~lt::torrent_flags::auto_managed;
}
else
{
// Torrent can be actually "running" but temporarily "paused" to perform some
// service jobs behind the scenes so we need to restore it as "running"
if (resumeData.operatingMode == BitTorrent::TorrentOperatingMode::AutoManaged)
{
p.flags |= lt::torrent_flags::auto_managed;
}
else
{
p.flags &= ~lt::torrent_flags::paused;
p.flags &= ~lt::torrent_flags::auto_managed;
}
}
QVector<Column> columns {
DB_COLUMN_TORRENT_ID,
DB_COLUMN_NAME,
DB_COLUMN_CATEGORY,
DB_COLUMN_TAGS,
DB_COLUMN_TARGET_SAVE_PATH,
DB_COLUMN_CONTENT_LAYOUT,
DB_COLUMN_RATIO_LIMIT,
DB_COLUMN_SEEDING_TIME_LIMIT,
DB_COLUMN_HAS_OUTER_PIECES_PRIORITY,
DB_COLUMN_HAS_SEED_STATUS,
DB_COLUMN_OPERATING_MODE,
DB_COLUMN_STOPPED,
DB_COLUMN_RESUMEDATA
};
// 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)
{
try
{
const auto torrentCreator = lt::create_torrent(*torrentInfo);
const lt::entry metadata = torrentCreator.generate();
lt::bencode(std::back_inserter(bencodedMetadata), metadata);
}
catch (const std::exception &err)
{
LogMsg(tr("Couldn't save torrent metadata. Error: %1.")
.arg(QString::fromLocal8Bit(err.what())), Log::CRITICAL);
return;
}
columns.append(DB_COLUMN_METADATA);
}
QByteArray bencodedResumeData;
bencodedResumeData.reserve(256 * 1024);
lt::bencode(std::back_inserter(bencodedResumeData), lt::write_resume_data(p));
const QString insertTorrentStatement = makeInsertStatement(DB_TABLE_TORRENTS, columns)
+ makeOnConflictUpdateStatement(DB_COLUMN_TORRENT_ID, columns);
auto db = QSqlDatabase::database(m_connectionName);
QSqlQuery query {db};
try
{
if (!query.prepare(insertTorrentStatement))
throw RuntimeError(query.lastError().text());
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, id.toString());
query.bindValue(DB_COLUMN_NAME.placeholder, resumeData.name);
query.bindValue(DB_COLUMN_CATEGORY.placeholder, resumeData.category);
query.bindValue(DB_COLUMN_TAGS.placeholder, (resumeData.tags.isEmpty()
? QVariant(QVariant::String) : resumeData.tags.join(QLatin1String(","))));
query.bindValue(DB_COLUMN_TARGET_SAVE_PATH.placeholder, Profile::instance()->toPortablePath(resumeData.savePath));
query.bindValue(DB_COLUMN_CONTENT_LAYOUT.placeholder, Utils::String::fromEnum(resumeData.contentLayout));
query.bindValue(DB_COLUMN_RATIO_LIMIT.placeholder, static_cast<int>(resumeData.ratioLimit * 1000));
query.bindValue(DB_COLUMN_SEEDING_TIME_LIMIT.placeholder, resumeData.seedingTimeLimit);
query.bindValue(DB_COLUMN_HAS_OUTER_PIECES_PRIORITY.placeholder, resumeData.firstLastPiecePriority);
query.bindValue(DB_COLUMN_HAS_SEED_STATUS.placeholder, resumeData.hasSeedStatus);
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)
query.bindValue(DB_COLUMN_METADATA.placeholder, bencodedMetadata);
if (!query.exec())
throw RuntimeError(query.lastError().text());
}
catch (const RuntimeError &err)
{
LogMsg(tr("Couldn't store resume data for torrent '%1'. Error: %2")
.arg(id.toString(), err.message()), Log::CRITICAL);
}
}
void BitTorrent::DBResumeDataStorage::Worker::remove(const TorrentID &id) const
{
const auto deleteTorrentStatement = QString::fromLatin1("DELETE FROM %1 WHERE %2 = %3;")
.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_TORRENT_ID.name), DB_COLUMN_TORRENT_ID.placeholder);
auto db = QSqlDatabase::database(m_connectionName);
QSqlQuery query {db};
try
{
if (!query.prepare(deleteTorrentStatement))
throw RuntimeError(query.lastError().text());
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, id.toString());
if (!query.exec())
throw RuntimeError(query.lastError().text());
}
catch (const RuntimeError &err)
{
LogMsg(tr("Couldn't delete resume data of torrent '%1'. Error: %2")
.arg(id.toString(), err.message()), Log::CRITICAL);
}
}
void BitTorrent::DBResumeDataStorage::Worker::storeQueue(const QVector<TorrentID> &queue) const
{
const auto updateQueuePosStatement = QString::fromLatin1("UPDATE %1 SET %2 = %3 WHERE %4 = %5;")
.arg(quoted(DB_TABLE_TORRENTS), quoted(DB_COLUMN_QUEUE_POSITION.name), DB_COLUMN_QUEUE_POSITION.placeholder
, quoted(DB_COLUMN_TORRENT_ID.name), DB_COLUMN_TORRENT_ID.placeholder);
auto db = QSqlDatabase::database(m_connectionName);
try
{
if (!db.transaction())
throw RuntimeError(db.lastError().text());
QSqlQuery query {db};
try
{
if (!query.prepare(updateQueuePosStatement))
throw RuntimeError(query.lastError().text());
int pos = 0;
for (const TorrentID &torrentID : queue)
{
query.bindValue(DB_COLUMN_TORRENT_ID.placeholder, torrentID.toString());
query.bindValue(DB_COLUMN_QUEUE_POSITION.placeholder, pos++);
if (!query.exec())
throw RuntimeError(query.lastError().text());
}
if (!db.commit())
throw RuntimeError(db.lastError().text());
}
catch (const RuntimeError &)
{
db.rollback();
throw;
}
}
catch (const RuntimeError &err)
{
LogMsg(tr("Couldn't store torrents queue positions. Error: %1")
.arg(err.message()), Log::CRITICAL);
}
}

View File

@@ -1,60 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include "resumedatastorage.h"
class QThread;
namespace BitTorrent
{
class DBResumeDataStorage final : public ResumeDataStorage
{
Q_OBJECT
Q_DISABLE_COPY(DBResumeDataStorage)
public:
explicit DBResumeDataStorage(const QString &dbPath, QObject *parent = nullptr);
~DBResumeDataStorage() override;
QVector<TorrentID> registeredTorrents() const override;
std::optional<LoadTorrentParams> load(const TorrentID &id) const override;
void store(const TorrentID &id, const LoadTorrentParams &resumeData) const override;
void remove(const TorrentID &id) const override;
void storeQueue(const QVector<TorrentID> &queue) const override;
private:
void createDB() const;
QThread *m_ioThread = nullptr;
class Worker;
Worker *m_asyncWorker = nullptr;
};
}

View File

@@ -41,24 +41,6 @@ bool BitTorrent::InfoHash::isValid() const
return m_valid;
}
SHA1Hash BitTorrent::InfoHash::v1() const
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
return (m_nativeHash.has_v1() ? SHA1Hash(m_nativeHash.v1) : SHA1Hash());
#else
return {m_nativeHash};
#endif
}
SHA256Hash BitTorrent::InfoHash::v2() const
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
return (m_nativeHash.has_v2() ? SHA256Hash(m_nativeHash.v2) : SHA256Hash());
#else
return {};
#endif
}
BitTorrent::TorrentID BitTorrent::InfoHash::toTorrentID() const
{
#if (LIBTORRENT_VERSION_NUM >= 20000)

View File

@@ -69,8 +69,6 @@ namespace BitTorrent
InfoHash(const WrappedType &nativeHash);
bool isValid() const;
SHA1Hash v1() const;
SHA256Hash v2() const;
TorrentID toTorrentID() const;
operator WrappedType() const;

View File

@@ -34,6 +34,7 @@
#include <libtorrent/sha1_hash.hpp>
#include <QRegularExpression>
#include <QUrl>
#include "infohash.h"
@@ -58,8 +59,6 @@ namespace
using namespace BitTorrent;
const int magnetUriId = qRegisterMetaType<MagnetUri>();
MagnetUri::MagnetUri(const QString &source)
: m_valid(false)
, m_url(source)
@@ -83,13 +82,13 @@ MagnetUri::MagnetUri(const QString &source)
m_name = QString::fromStdString(m_addTorrentParams.name);
m_trackers.reserve(static_cast<decltype(m_trackers)::size_type>(m_addTorrentParams.trackers.size()));
m_trackers.reserve(m_addTorrentParams.trackers.size());
for (const std::string &tracker : m_addTorrentParams.trackers)
m_trackers.append({QString::fromStdString(tracker)});
m_urlSeeds.reserve(static_cast<decltype(m_urlSeeds)::size_type>(m_addTorrentParams.url_seeds.size()));
m_urlSeeds.reserve(m_addTorrentParams.url_seeds.size());
for (const std::string &urlSeed : m_addTorrentParams.url_seeds)
m_urlSeeds.append(QString::fromStdString(urlSeed));
m_urlSeeds.append(QUrl(QString::fromStdString(urlSeed)));
}
bool MagnetUri::isValid() const

View File

@@ -31,12 +31,13 @@
#include <libtorrent/add_torrent_params.hpp>
#include <QString>
#include <QUrl>
#include <QVector>
#include "infohash.h"
#include "trackerentry.h"
class QUrl;
namespace BitTorrent
{
class MagnetUri
@@ -63,5 +64,3 @@ namespace BitTorrent
lt::add_torrent_params m_addTorrentParams;
};
}
Q_DECLARE_METATYPE(BitTorrent::MagnetUri)

View File

@@ -257,86 +257,123 @@ qreal PeerInfo::relevance() const
void PeerInfo::determineFlags()
{
const auto updateFlags = [this](const QChar specifier, const QString &explanation)
{
m_flags += (specifier + QLatin1Char(' '));
m_flagsDescription += QString::fromLatin1("%1 = %2\n").arg(specifier, explanation);
};
if (isInteresting())
{
// d = Your client wants to download, but peer doesn't want to send (interested and choked)
if (isRemoteChocked())
{
// d = Your client wants to download, but peer doesn't want to send (interested and choked)
updateFlags(QLatin1Char('d'), tr("Interested (local) and choked (peer)"));
m_flags += "d ";
m_flagsDescription += ("d = "
+ tr("Interested(local) and Choked(peer)") + '\n');
}
else
{
// D = Currently downloading (interested and not choked)
updateFlags(QLatin1Char('D'), tr("Interested (local) and unchoked (peer)"));
m_flags += "D ";
m_flagsDescription += ("D = "
+ tr("interested(local) and unchoked(peer)") + '\n');
}
}
if (isRemoteInterested())
{
// u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
if (isChocked())
{
// u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
updateFlags(QLatin1Char('u'), tr("Interested (peer) and choked (local)"));
m_flags += "u ";
m_flagsDescription += ("u = "
+ tr("interested(peer) and choked(local)") + '\n');
}
else
{
// U = Currently uploading (interested and not choked)
updateFlags(QLatin1Char('U'), tr("Interested (peer) and unchoked (local)"));
m_flags += "U ";
m_flagsDescription += ("U = "
+ tr("interested(peer) and unchoked(local)") + '\n');
}
}
// O = Optimistic unchoke
if (optimisticUnchoke())
{
m_flags += "O ";
m_flagsDescription += ("O = " + tr("optimistic unchoke") + '\n');
}
// S = Peer is snubbed
if (isSnubbed())
{
m_flags += "S ";
m_flagsDescription += ("S = " + tr("peer snubbed") + '\n');
}
// I = Peer is an incoming connection
if (!isLocalConnection())
{
m_flags += "I ";
m_flagsDescription += ("I = " + tr("incoming connection") + '\n');
}
// K = Peer is unchoking your client, but your client is not interested
if (!isRemoteChocked() && !isInteresting())
updateFlags(QLatin1Char('K'), tr("Not interested (local) and unchoked (peer)"));
{
m_flags += "K ";
m_flagsDescription += ("K = "
+ tr("not interested(local) and unchoked(peer)") + '\n');
}
// ? = Your client unchoked the peer but the peer is not interested
if (!isChocked() && !isRemoteInterested())
updateFlags(QLatin1Char('?'), tr("Not interested (peer) and unchoked (local)"));
// O = Optimistic unchoke
if (optimisticUnchoke())
updateFlags(QLatin1Char('O'), tr("Optimistic unchoke"));
// S = Peer is snubbed
if (isSnubbed())
updateFlags(QLatin1Char('S'), tr("Peer snubbed"));
// I = Peer is an incoming connection
if (!isLocalConnection())
updateFlags(QLatin1Char('I'), tr("Incoming connection"));
// H = Peer was obtained through DHT
if (fromDHT())
updateFlags(QLatin1Char('H'), tr("Peer from DHT"));
{
m_flags += "? ";
m_flagsDescription += ("? = "
+ tr("not interested(peer) and unchoked(local)") + '\n');
}
// X = Peer was included in peerlists obtained through Peer Exchange (PEX)
if (fromPeX())
updateFlags(QLatin1Char('X'), tr("Peer from PEX"));
{
m_flags += "X ";
m_flagsDescription += ("X = " + tr("peer from PEX") + '\n');
}
// L = Peer is local
if (fromLSD())
updateFlags(QLatin1Char('L'), tr("Peer from LSD"));
// H = Peer was obtained through DHT
if (fromDHT())
{
m_flags += "H ";
m_flagsDescription += ("H = " + tr("peer from DHT") + '\n');
}
// E = Peer is using Protocol Encryption (all traffic)
if (isRC4Encrypted())
updateFlags(QLatin1Char('E'), tr("Encrypted traffic"));
{
m_flags += "E ";
m_flagsDescription += ("E = " + tr("encrypted traffic") + '\n');
}
// e = Peer is using Protocol Encryption (handshake)
if (isPlaintextEncrypted())
updateFlags(QLatin1Char('e'), tr("Encrypted handshake"));
{
m_flags += "e ";
m_flagsDescription += ("e = " + tr("encrypted handshake") + '\n');
}
// P = Peer is using uTorrent uTP
if (useUTPSocket())
updateFlags(QLatin1Char('P'), QString::fromUtf8(C_UTP));
{
m_flags += "P ";
m_flagsDescription += ("P = " + QString::fromUtf8(C_UTP) + '\n');
}
m_flags.chop(1);
m_flagsDescription.chop(1);
// L = Peer is local
if (fromLSD())
{
m_flags += "L ";
m_flagsDescription += ("L = " + tr("peer from LSD") + '\n');
}
m_flags = m_flags.trimmed();
m_flagsDescription = m_flagsDescription.trimmed();
}
QString PeerInfo::flags() const

View File

@@ -0,0 +1,83 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "resumedatasavingmanager.h"
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#include <QByteArray>
#include <QSaveFile>
#include "base/logger.h"
#include "base/utils/fs.h"
#include "base/utils/io.h"
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
: m_resumeDataDir(resumeFolderPath)
{
}
void ResumeDataSavingManager::save(const QString &filename, const QByteArray &data) const
{
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
QSaveFile file {filepath};
if (!file.open(QIODevice::WriteOnly) || (file.write(data) != data.size()) || !file.commit())
{
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::CRITICAL);
}
}
void ResumeDataSavingManager::save(const QString &filename, const std::shared_ptr<lt::entry> &data) const
{
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
QSaveFile file {filepath};
if (!file.open(QIODevice::WriteOnly))
{
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::CRITICAL);
return;
}
lt::bencode(Utils::IO::FileDeviceOutputIterator {file}, *data);
if ((file.error() != QFileDevice::NoError) || !file.commit())
{
LogMsg(tr("Couldn't save data to '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::CRITICAL);
}
}
void ResumeDataSavingManager::remove(const QString &filename) const
{
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
Utils::Fs::forceRemove(filepath);
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2021 Mike Tzou (Chocobo1)
* Copyright (C) 2015, 2018 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,22 +28,28 @@
#pragma once
#include <QMetaType>
#include <QString>
#include <memory>
#include "base/orderedset.h"
#include "base/utils/compare.h"
#include <libtorrent/fwd.hpp>
class TagLessThan
#include <QDir>
#include <QObject>
class QByteArray;
class ResumeDataSavingManager : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(ResumeDataSavingManager)
public:
bool operator()(const QString &left, const QString &right) const;
explicit ResumeDataSavingManager(const QString &resumeFolderPath);
public slots:
void save(const QString &filename, const QByteArray &data) const;
void save(const QString &filename, const std::shared_ptr<lt::entry> &data) const;
void remove(const QString &filename) const;
private:
Utils::Compare::NaturalCompare<Qt::CaseInsensitive> m_compare;
Utils::Compare::NaturalCompare<Qt::CaseSensitive> m_subCompare;
const QDir m_resumeDataDir;
};
using TagSet = OrderedSet<QString, TagLessThan>;
Q_DECLARE_METATYPE(TagSet)

View File

@@ -1,55 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include <optional>
#include <QtContainerFwd>
#include <QObject>
namespace BitTorrent
{
class TorrentID;
struct LoadTorrentParams;
class ResumeDataStorage : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(ResumeDataStorage)
public:
using QObject::QObject;
virtual QVector<TorrentID> registeredTorrents() const = 0;
virtual std::optional<LoadTorrentParams> load(const TorrentID &id) const = 0;
virtual void store(const TorrentID &id, const LoadTorrentParams &resumeData) const = 0;
virtual void remove(const TorrentID &id) const = 0;
virtual void storeQueue(const QVector<TorrentID> &queue) const = 0;
};
}

View File

@@ -42,25 +42,28 @@
#endif
#include <libtorrent/alert_types.hpp>
#include <libtorrent/bdecode.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/error_code.hpp>
#include <libtorrent/extensions/smart_ban.hpp>
#include <libtorrent/extensions/ut_metadata.hpp>
#include <libtorrent/extensions/ut_pex.hpp>
#include <libtorrent/ip_filter.hpp>
#include <libtorrent/magnet_uri.hpp>
#include <libtorrent/read_resume_data.hpp>
#include <libtorrent/session.hpp>
#include <libtorrent/session_stats.hpp>
#include <libtorrent/session_status.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/write_resume_data.hpp>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QHostAddress>
#include <QNetworkAddressEntry>
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
#include <QNetworkConfigurationManager>
#endif
#include <QNetworkInterface>
#include <QRegularExpression>
#include <QString>
@@ -85,27 +88,33 @@
#include "base/utils/random.h"
#include "base/version.h"
#include "bandwidthscheduler.h"
#include "bencoderesumedatastorage.h"
#include "common.h"
#include "customstorage.h"
#include "dbresumedatastorage.h"
#include "filesearcher.h"
#include "filterparserthread.h"
#include "loadtorrentparams.h"
#include "ltunderlyingtype.h"
#include "magneturi.h"
#include "nativesessionextension.h"
#include "portforwarderimpl.h"
#include "resumedatasavingmanager.h"
#include "statistics.h"
#include "torrentimpl.h"
#include "tracker.h"
#include "trackerentry.h"
static const char PEER_ID[] = "qB";
static const char RESUME_FOLDER[] = "BT_backup";
static const char USER_AGENT[] = "qBittorrent/" QBT_VERSION_2;
using namespace BitTorrent;
namespace
{
const char PEER_ID[] = "qB";
const char USER_AGENT[] = "qBittorrent/" QBT_VERSION_2;
template <typename LTStr>
QString fromLTString(const LTStr &str)
{
return QString::fromUtf8(str.data(), static_cast<int>(str.size()));
}
void torrentQueuePositionUp(const lt::torrent_handle &handle)
{
@@ -296,6 +305,17 @@ namespace
};
}
using ListType = lt::entry::list_type;
ListType setToEntryList(const QSet<QString> &input)
{
ListType entryList;
entryList.reserve(input.size());
for (const QString &setValue : input)
entryList.emplace_back(setValue.toStdString());
return entryList;
}
#ifdef Q_OS_WIN
QString convertIfaceNameToGuid(const QString &name)
{
@@ -318,8 +338,6 @@ namespace
#endif
}
const int addTorrentParamsId = qRegisterMetaType<AddTorrentParams>();
// Session
Session *Session::m_instance = nullptr;
@@ -339,7 +357,7 @@ Session::Session(QObject *parent)
, m_announceToAllTiers(BITTORRENT_SESSION_KEY("AnnounceToAllTiers"), true)
, m_asyncIOThreads(BITTORRENT_SESSION_KEY("AsyncIOThreadsCount"), 10)
, m_hashingThreads(BITTORRENT_SESSION_KEY("HashingThreadsCount"), 2)
, m_filePoolSize(BITTORRENT_SESSION_KEY("FilePoolSize"), 5000)
, m_filePoolSize(BITTORRENT_SESSION_KEY("FilePoolSize"), 40)
, m_checkingMemUsage(BITTORRENT_SESSION_KEY("CheckingMemUsageSize"), 32)
, m_diskCacheSize(BITTORRENT_SESSION_KEY("DiskCacheSize"), -1)
, m_diskCacheTTL(BITTORRENT_SESSION_KEY("DiskCacheTTL"), 60)
@@ -354,7 +372,6 @@ Session::Session(QObject *parent)
, m_sendBufferWatermark(BITTORRENT_SESSION_KEY("SendBufferWatermark"), 500)
, m_sendBufferLowWatermark(BITTORRENT_SESSION_KEY("SendBufferLowWatermark"), 10)
, m_sendBufferWatermarkFactor(BITTORRENT_SESSION_KEY("SendBufferWatermarkFactor"), 50)
, m_connectionSpeed(BITTORRENT_SESSION_KEY("ConnectionSpeed"), 30)
, m_socketBacklogSize(BITTORRENT_SESSION_KEY("SocketBacklogSize"), 30)
, m_isAnonymousModeEnabled(BITTORRENT_SESSION_KEY("AnonymousModeEnabled"), false)
, m_isQueueingEnabled(BITTORRENT_SESSION_KEY("QueueingSystemEnabled"), false)
@@ -373,7 +390,6 @@ Session::Session(QObject *parent)
, m_includeOverheadInLimits(BITTORRENT_SESSION_KEY("IncludeOverheadInLimits"), false)
, m_announceIP(BITTORRENT_SESSION_KEY("AnnounceIP"))
, m_maxConcurrentHTTPAnnounces(BITTORRENT_SESSION_KEY("MaxConcurrentHTTPAnnounces"), 50)
, m_isReannounceWhenAddressChangedEnabled(BITTORRENT_SESSION_KEY("ReannounceWhenAddressChanged"), false)
, m_stopTrackerTimeout(BITTORRENT_SESSION_KEY("StopTrackerTimeout"), 5)
, m_maxConnections(BITTORRENT_SESSION_KEY("MaxConnections"), 500, lowerLimited(0, -1))
, m_maxUploads(BITTORRENT_SESSION_KEY("MaxUploads"), 20, lowerLimited(0, -1))
@@ -441,22 +457,22 @@ Session::Session(QObject *parent)
return tmp;
}
)
, m_resumeDataStorageType(BITTORRENT_SESSION_KEY("ResumeDataStorageType"), ResumeDataStorageType::Legacy)
#if defined(Q_OS_WIN)
, m_OSMemoryPriority(BITTORRENT_KEY("OSMemoryPriority"), OSMemoryPriority::BelowNormal)
#endif
, m_resumeFolderLock {new QFile {this}}
, m_seedingLimitTimer {new QTimer {this}}
, m_resumeDataTimer {new QTimer {this}}
, m_statistics {new Statistics {this}}
, m_ioThread {new QThread {this}}
, m_recentErroredTorrentsTimer {new QTimer {this}}
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
, m_networkManager {new QNetworkConfigurationManager {this}}
#endif
{
if (port() < 0)
m_port = Utils::Random::rand(1024, 65535);
initResumeFolder();
m_recentErroredTorrentsTimer->setSingleShot(true);
m_recentErroredTorrentsTimer->setInterval(1000);
connect(m_recentErroredTorrentsTimer, &QTimer::timeout
@@ -479,8 +495,7 @@ Session::Session(QObject *parent)
m_storedCategories = map_cast(m_categories);
}
const QStringList storedTags = m_storedTags.get();
m_tags = {storedTags.cbegin(), storedTags.cend()};
m_tags = List::toSet(m_storedTags.get());
enqueueRefresh();
updateSeedingLimitTimer();
@@ -492,13 +507,15 @@ Session::Session(QObject *parent)
, &Net::ProxyConfigurationManager::proxyConfigurationChanged
, this, &Session::configureDeferred);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
// Network configuration monitor
connect(m_networkManager, &QNetworkConfigurationManager::onlineStateChanged, this, &Session::networkOnlineStateChanged);
connect(m_networkManager, &QNetworkConfigurationManager::configurationAdded, this, &Session::networkConfigurationChange);
connect(m_networkManager, &QNetworkConfigurationManager::configurationRemoved, this, &Session::networkConfigurationChange);
connect(m_networkManager, &QNetworkConfigurationManager::configurationChanged, this, &Session::networkConfigurationChange);
#endif
m_resumeDataSavingManager = new ResumeDataSavingManager {m_resumeFolderPath};
m_resumeDataSavingManager->moveToThread(m_ioThread);
connect(m_ioThread, &QThread::finished, m_resumeDataSavingManager, &QObject::deleteLater);
m_fileSearcher = new FileSearcher;
m_fileSearcher->moveToThread(m_ioThread);
@@ -838,13 +855,17 @@ bool Session::hasTag(const QString &tag) const
bool Session::addTag(const QString &tag)
{
if (!isValidTag(tag) || hasTag(tag))
if (!isValidTag(tag))
return false;
m_tags.insert(tag);
m_storedTags = m_tags.values();
emit tagAdded(tag);
return true;
if (!hasTag(tag))
{
m_tags.insert(tag);
m_storedTags = m_tags.values();
emit tagAdded(tag);
return true;
}
return false;
}
bool Session::removeTag(const QString &tag)
@@ -980,6 +1001,9 @@ Session::~Session()
m_ioThread->quit();
m_ioThread->wait();
m_resumeFolderLock->close();
m_resumeFolderLock->remove();
}
void Session::initInstance()
@@ -1011,9 +1035,9 @@ void Session::adjustLimits()
void Session::applyBandwidthLimits()
{
lt::settings_pack settingsPack = m_nativeSession->get_settings();
applyBandwidthLimits(settingsPack);
m_nativeSession->apply_settings(settingsPack);
lt::settings_pack settingsPack = m_nativeSession->get_settings();
applyBandwidthLimits(settingsPack);
m_nativeSession->apply_settings(settingsPack);
}
void Session::configure()
@@ -1071,6 +1095,7 @@ void Session::initializeNativeSession()
// Speed up exit
pack.set_int(lt::settings_pack::auto_scrape_interval, 1200); // 20 minutes
pack.set_int(lt::settings_pack::auto_scrape_min_interval, 900); // 15 minutes
pack.set_int(lt::settings_pack::connection_speed, 20); // default is 10
// libtorrent 1.1 enables UPnP & NAT-PMP by default
// turn them off before `lt::session` ctor to avoid split second effects
pack.set_bool(lt::settings_pack::enable_upnp, false);
@@ -1187,8 +1212,6 @@ void Session::initMetrics()
void Session::loadLTSettings(lt::settings_pack &settingsPack)
{
settingsPack.set_int(lt::settings_pack::connection_speed, connectionSpeed());
// from libtorrent doc:
// It will not take affect until the listen_interfaces settings is updated
settingsPack.set_int(lt::settings_pack::listen_queue_size, socketBacklogSize());
@@ -1842,7 +1865,13 @@ bool Session::deleteTorrent(const TorrentID &id, const DeleteOption deleteOption
}
// Remove it from torrent resume directory
m_resumeDataStorage->remove(torrent->id());
const QString resumedataFile = QString::fromLatin1("%1.fastresume").arg(torrent->id().toString());
const QString metadataFile = QString::fromLatin1("%1.torrent").arg(torrent->id().toString());
QMetaObject::invokeMethod(m_resumeDataSavingManager, [this, resumedataFile, metadataFile]()
{
m_resumeDataSavingManager->remove(resumedataFile);
m_resumeDataSavingManager->remove(metadataFile);
});
delete torrent;
return true;
@@ -2052,21 +2081,20 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr
LoadTorrentParams loadTorrentParams;
loadTorrentParams.name = addTorrentParams.name;
loadTorrentParams.tags = addTorrentParams.tags;
loadTorrentParams.firstLastPiecePriority = addTorrentParams.firstLastPiecePriority;
loadTorrentParams.hasSeedStatus = addTorrentParams.skipChecking; // do not react on 'torrent_finished_alert' when skipping
loadTorrentParams.contentLayout = addTorrentParams.contentLayout.value_or(torrentContentLayout());
loadTorrentParams.operatingMode = (addTorrentParams.addForced ? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged);
loadTorrentParams.stopped = addTorrentParams.addPaused.value_or(isAddTorrentPaused());
loadTorrentParams.forced = addTorrentParams.addForced;
loadTorrentParams.paused = addTorrentParams.addPaused.value_or(isAddTorrentPaused());
loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!isAutoTMMDisabledByDefault());
if (useAutoTMM)
loadTorrentParams.savePath = "";
else if (addTorrentParams.savePath.isEmpty())
else if (addTorrentParams.savePath.trimmed().isEmpty())
loadTorrentParams.savePath = defaultSavePath();
else if (QDir(addTorrentParams.savePath).isRelative())
loadTorrentParams.savePath = QDir(defaultSavePath()).absoluteFilePath(addTorrentParams.savePath);
else
loadTorrentParams.savePath = normalizePath(addTorrentParams.savePath);
@@ -2076,12 +2104,6 @@ LoadTorrentParams Session::initLoadTorrentParams(const AddTorrentParams &addTorr
else
loadTorrentParams.category = addTorrentParams.category;
for (const QString &tag : addTorrentParams.tags)
{
if (hasTag(tag) || addTag(tag))
loadTorrentParams.tags.insert(tag);
}
return loadTorrentParams;
}
@@ -2183,11 +2205,11 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
else
p.flags &= ~lt::torrent_flags::seed_mode;
if (loadTorrentParams.stopped || (loadTorrentParams.operatingMode == TorrentOperatingMode::AutoManaged))
if (loadTorrentParams.paused || !loadTorrentParams.forced)
p.flags |= lt::torrent_flags::paused;
else
p.flags &= ~lt::torrent_flags::paused;
if (loadTorrentParams.stopped || (loadTorrentParams.operatingMode == TorrentOperatingMode::Forced))
if (loadTorrentParams.paused || loadTorrentParams.forced)
p.flags &= ~lt::torrent_flags::auto_managed;
else
p.flags |= lt::torrent_flags::auto_managed;
@@ -2302,28 +2324,23 @@ void Session::exportTorrentFile(const Torrent *torrent, TorrentExportFolder fold
((folder == TorrentExportFolder::Finished) && !finishedTorrentExportDirectory().isEmpty()));
const QString validName = Utils::Fs::toValidFileSystemName(torrent->name());
const QString torrentFilename = QString::fromLatin1("%1.torrent").arg(torrent->id().toString());
QString torrentExportFilename = QString::fromLatin1("%1.torrent").arg(validName);
const QString torrentPath = QDir(m_resumeFolderPath).absoluteFilePath(torrentFilename);
const QDir exportPath(folder == TorrentExportFolder::Regular ? torrentExportDirectory() : finishedTorrentExportDirectory());
if (exportPath.exists() || exportPath.mkpath(exportPath.absolutePath()))
{
QString newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
int counter = 0;
while (QFile::exists(newTorrentPath))
while (QFile::exists(newTorrentPath) && !Utils::Fs::sameFiles(torrentPath, newTorrentPath))
{
// Append number to torrent name to make it unique
torrentExportFilename = QString::fromLatin1("%1 %2.torrent").arg(validName).arg(++counter);
newTorrentPath = exportPath.absoluteFilePath(torrentExportFilename);
}
try
{
torrent->info().saveToFile(newTorrentPath);
}
catch (const RuntimeError &err)
{
LogMsg(tr("Couldn't export torrent metadata file '%1'. Reason: %2.")
.arg(newTorrentPath, err.message()), Log::WARNING);
}
if (!QFile::exists(newTorrentPath))
QFile::copy(torrentPath, newTorrentPath);
}
}
@@ -2376,25 +2393,31 @@ void Session::saveResumeData()
void Session::saveTorrentsQueue() const
{
QVector<TorrentID> queue;
// store hash in textual representation
QMap<int, QString> queue; // Use QMap since it should be ordered by key
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());
if (queuePos >= 0)
{
if (queuePos >= queue.size())
queue.resize(queuePos + 1);
queue[queuePos] = torrent->id();
}
queue[queuePos] = torrent->id().toString();
}
m_resumeDataStorage->storeQueue(queue);
QByteArray data;
data.reserve(((TorrentID::length() * 2) + 1) * queue.size());
for (const QString &torrentID : asConst(queue))
data += (torrentID.toLatin1() + '\n');
const QString filename = QLatin1String {"queue"};
QMetaObject::invokeMethod(m_resumeDataSavingManager
, [this, data, filename]() { m_resumeDataSavingManager->save(filename, data); });
}
void Session::removeTorrentsQueue() const
{
m_resumeDataStorage->storeQueue({});
const QString filename = QLatin1String {"queue"};
QMetaObject::invokeMethod(m_resumeDataSavingManager
, [this, filename]() { m_resumeDataSavingManager->remove(filename); });
}
void Session::setDefaultSavePath(QString path)
@@ -2423,7 +2446,6 @@ void Session::setTempPath(QString path)
torrent->handleTempPathChanged();
}
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
void Session::networkOnlineStateChanged(const bool online)
{
LogMsg(tr("System network status changed to %1", "e.g: System network status changed to ONLINE").arg(online ? tr("ONLINE") : tr("OFFLINE")), Log::INFO);
@@ -2444,7 +2466,6 @@ void Session::networkConfigurationChange(const QNetworkConfiguration &cfg)
configureListeningInterface();
}
}
#endif
QStringList Session::getListeningIPs() const
{
@@ -2748,9 +2769,6 @@ void Session::setPort(const int port)
{
m_port = port;
configureListeningInterface();
if (isReannounceWhenAddressChangedEnabled())
reannounceToAllTrackers();
}
}
@@ -2950,16 +2968,6 @@ void Session::setBannedIPs(const QStringList &newList)
configureDeferred();
}
ResumeDataStorageType Session::resumeDataStorageType() const
{
return m_resumeDataStorageType;
}
void Session::setResumeDataStorageType(const ResumeDataStorageType type)
{
m_resumeDataStorageType = type;
}
QStringList Session::bannedIPs() const
{
return m_bannedIPs;
@@ -3041,6 +3049,7 @@ void Session::setMaxConnectionsPerTorrent(int max)
// Apply this to all session torrents
for (const lt::torrent_handle &handle : m_nativeSession->get_torrents())
{
if (!handle.is_valid()) continue;
try
{
handle.set_max_connections(max);
@@ -3065,6 +3074,7 @@ void Session::setMaxUploadsPerTorrent(int max)
// Apply this to all session torrents
for (const lt::torrent_handle &handle : m_nativeSession->get_torrents())
{
if (!handle.is_valid()) continue;
try
{
handle.set_max_uploads(max);
@@ -3334,19 +3344,6 @@ void Session::setSendBufferWatermarkFactor(const int value)
configureDeferred();
}
int Session::connectionSpeed() const
{
return m_connectionSpeed;
}
void Session::setConnectionSpeed(const int value)
{
if (value == m_connectionSpeed) return;
m_connectionSpeed = value;
configureDeferred();
}
int Session::socketBacklogSize() const
{
return m_socketBacklogSize;
@@ -3608,25 +3605,6 @@ void Session::setMaxConcurrentHTTPAnnounces(const int value)
configureDeferred();
}
bool Session::isReannounceWhenAddressChangedEnabled() const
{
return m_isReannounceWhenAddressChangedEnabled;
}
void Session::setReannounceWhenAddressChangedEnabled(const bool enabled)
{
if (enabled == m_isReannounceWhenAddressChangedEnabled)
return;
m_isReannounceWhenAddressChangedEnabled = enabled;
}
void Session::reannounceToAllTrackers() const
{
for (const lt::torrent_handle &torrent : m_nativeSession->get_torrents())
torrent.force_reannounce(0, -1, lt::torrent_handle::ignore_min_interval);
}
int Session::stopTrackerTimeout() const
{
return m_stopTrackerTimeout;
@@ -3818,13 +3796,14 @@ void Session::updateSeedingLimitTimer()
}
}
void Session::handleTorrentShareLimitChanged(TorrentImpl *const)
void Session::handleTorrentShareLimitChanged(TorrentImpl *const torrent)
{
updateSeedingLimitTimer();
}
void Session::handleTorrentNameChanged(TorrentImpl *const)
void Session::handleTorrentNameChanged(TorrentImpl *const torrent)
{
Q_UNUSED(torrent);
}
void Session::handleTorrentSavePathChanged(TorrentImpl *const torrent)
@@ -3891,9 +3870,21 @@ void Session::handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QVe
void Session::handleTorrentMetadataReceived(TorrentImpl *const torrent)
{
// Copy the torrent file to the export folder
if (!torrentExportDirectory().isEmpty())
exportTorrentFile(torrent);
// Save metadata
const QDir resumeDataDir {m_resumeFolderPath};
const QString torrentFileName {QString {"%1.torrent"}.arg(torrent->id().toString())};
try
{
torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName));
// Copy the torrent file to the export folder
if (!torrentExportDirectory().isEmpty())
exportTorrentFile(torrent);
}
catch (const RuntimeError &err)
{
LogMsg(tr("Couldn't save torrent metadata file '%1'. Reason: %2")
.arg(torrentFileName, err.message()), Log::CRITICAL);
}
emit torrentMetadataReceived(torrent);
}
@@ -3954,7 +3945,48 @@ void Session::handleTorrentResumeDataReady(TorrentImpl *const torrent, const Loa
{
--m_numResumeData;
m_resumeDataStorage->store(torrent->id(), data);
// We need to adjust native libtorrent resume data
lt::add_torrent_params p = data.ltAddTorrentParams;
p.save_path = Profile::instance()->toPortablePath(QString::fromStdString(p.save_path)).toStdString();
if (data.paused)
{
p.flags |= lt::torrent_flags::paused;
p.flags &= ~lt::torrent_flags::auto_managed;
}
else
{
// Torrent can be actually "running" but temporarily "paused" to perform some
// service jobs behind the scenes so we need to restore it as "running"
if (!data.forced)
{
p.flags |= lt::torrent_flags::auto_managed;
}
else
{
p.flags &= ~lt::torrent_flags::paused;
p.flags &= ~lt::torrent_flags::auto_managed;
}
}
// Separated thread is used for the blocking IO which results in slow processing of many torrents.
// Copying lt::entry objects around isn't cheap.
auto resumeDataPtr = std::make_shared<lt::entry>(lt::write_resume_data(p));
lt::entry &resumeData = *resumeDataPtr;
resumeData["qBt-savePath"] = Profile::instance()->toPortablePath(data.savePath).toStdString();
resumeData["qBt-ratioLimit"] = static_cast<int>(data.ratioLimit * 1000);
resumeData["qBt-seedingTimeLimit"] = data.seedingTimeLimit;
resumeData["qBt-category"] = data.category.toStdString();
resumeData["qBt-tags"] = setToEntryList(data.tags);
resumeData["qBt-name"] = data.name.toStdString();
resumeData["qBt-seedStatus"] = data.hasSeedStatus;
resumeData["qBt-contentLayout"] = Utils::String::fromEnum(data.contentLayout).toStdString();
resumeData["qBt-firstLastPiecePriority"] = data.firstLastPiecePriority;
const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->id().toString());
QMetaObject::invokeMethod(m_resumeDataSavingManager
, [this, filename, resumeDataPtr]() { m_resumeDataSavingManager->save(filename, resumeDataPtr); });
}
void Session::handleTorrentTrackerReply(TorrentImpl *const torrent, const QString &trackerUrl)
@@ -4087,6 +4119,28 @@ bool Session::hasPerTorrentSeedingTimeLimit() const
});
}
void Session::initResumeFolder()
{
m_resumeFolderPath = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + RESUME_FOLDER);
const QDir resumeFolderDir(m_resumeFolderPath);
if (resumeFolderDir.exists() || resumeFolderDir.mkpath(resumeFolderDir.absolutePath()))
{
m_resumeFolderLock->setFileName(resumeFolderDir.absoluteFilePath("session.lock"));
if (!m_resumeFolderLock->open(QFile::WriteOnly))
{
throw RuntimeError
{tr("Cannot write to torrent resume folder: \"%1\"")
.arg(Utils::Fs::toNativePath(m_resumeFolderPath))};
}
}
else
{
throw RuntimeError
{tr("Cannot create torrent resume folder: \"%1\"")
.arg(Utils::Fs::toNativePath(m_resumeFolderPath))};
}
}
void Session::configureDeferred()
{
if (m_deferredConfigureScheduled)
@@ -4165,60 +4219,159 @@ const CacheStatus &Session::cacheStatus() const
return m_cacheStatus;
}
void Session::startUpTorrents()
bool Session::loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata, LoadTorrentParams &torrentParams)
{
qDebug("Initializing torrents resume data storage...");
torrentParams = {};
const QString dbPath = Utils::Fs::expandPathAbs(
specialFolderLocation(SpecialFolder::Data) + QLatin1String("torrents.db"));
const bool dbStorageExists = QFile::exists(dbPath);
lt::error_code ec;
const lt::bdecode_node root = lt::bdecode(data, ec);
if (ec || (root.type() != lt::bdecode_node::dict_t)) return false;
ResumeDataStorage *startupStorage = nullptr;
if (resumeDataStorageType() == ResumeDataStorageType::SQLite)
torrentParams.restored = true;
torrentParams.category = fromLTString(root.dict_find_string_value("qBt-category"));
torrentParams.name = fromLTString(root.dict_find_string_value("qBt-name"));
torrentParams.savePath = Profile::instance()->fromPortablePath(
Utils::Fs::toUniformPath(fromLTString(root.dict_find_string_value("qBt-savePath"))));
torrentParams.hasSeedStatus = root.dict_find_int_value("qBt-seedStatus");
torrentParams.firstLastPiecePriority = root.dict_find_int_value("qBt-firstLastPiecePriority");
torrentParams.seedingTimeLimit = root.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME);
// TODO: The following code is deprecated. Replace with the commented one after several releases in 4.4.x.
// === BEGIN DEPRECATED CODE === //
const lt::bdecode_node contentLayoutNode = root.dict_find("qBt-contentLayout");
if (contentLayoutNode.type() == lt::bdecode_node::string_t)
{
m_resumeDataStorage = new DBResumeDataStorage(dbPath, this);
if (!dbStorageExists)
{
const QString dataPath = Utils::Fs::expandPathAbs(
specialFolderLocation(SpecialFolder::Data) + QLatin1String("BT_backup"));
startupStorage = new BencodeResumeDataStorage(dataPath, this);
}
const QString contentLayoutStr = fromLTString(contentLayoutNode.string_value());
torrentParams.contentLayout = Utils::String::toEnum(contentLayoutStr, TorrentContentLayout::Original);
}
else
{
const QString dataPath = Utils::Fs::expandPathAbs(
specialFolderLocation(SpecialFolder::Data) + QLatin1String("BT_backup"));
m_resumeDataStorage = new BencodeResumeDataStorage(dataPath, this);
const bool hasRootFolder = root.dict_find_int_value("qBt-hasRootFolder");
torrentParams.contentLayout = (hasRootFolder ? TorrentContentLayout::Original : TorrentContentLayout::NoSubfolder);
}
// === END DEPRECATED CODE === //
// === BEGIN REPLACEMENT CODE === //
// torrentParams.contentLayout = Utils::String::parse(
// fromLTString(root.dict_find_string_value("qBt-contentLayout")), TorrentContentLayout::Default);
// === END REPLACEMENT CODE === //
if (dbStorageExists)
startupStorage = new DBResumeDataStorage(dbPath, this);
const lt::string_view ratioLimitString = root.dict_find_string_value("qBt-ratioLimit");
if (ratioLimitString.empty())
torrentParams.ratioLimit = root.dict_find_int_value("qBt-ratioLimit", Torrent::USE_GLOBAL_RATIO * 1000) / 1000.0;
else
torrentParams.ratioLimit = fromLTString(ratioLimitString).toDouble();
const lt::bdecode_node tagsNode = root.dict_find("qBt-tags");
if (tagsNode.type() == lt::bdecode_node::list_t)
{
for (int i = 0; i < tagsNode.list_size(); ++i)
{
const QString tag = fromLTString(tagsNode.list_string_value_at(i));
if (Session::isValidTag(tag))
torrentParams.tags << tag;
}
}
if (!startupStorage)
startupStorage = m_resumeDataStorage;
// NOTE: Do we really need the following block in case of existing (restored) torrent?
torrentParams.savePath = normalizePath(torrentParams.savePath);
if (!torrentParams.category.isEmpty())
{
if (!m_categories.contains(torrentParams.category) && !addCategory(torrentParams.category))
torrentParams.category = "";
}
lt::add_torrent_params &p = torrentParams.ltAddTorrentParams;
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)
{
// If torrent has "stop_when_ready" flag set then it is actually "stopped"
torrentParams.paused = true;
torrentParams.forced = false;
// ...but temporarily "resumed" to perform some service jobs (e.g. checking)
p.flags &= ~lt::torrent_flags::paused;
p.flags |= lt::torrent_flags::auto_managed;
}
else
{
torrentParams.paused = (p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
torrentParams.forced = !(p.flags & lt::torrent_flags::paused) && !(p.flags & lt::torrent_flags::auto_managed);
}
const bool hasMetadata = (p.ti && p.ti->is_valid());
if (!hasMetadata && !root.dict_find("info-hash"))
return false;
return true;
}
// Will resume torrents in backup directory
void Session::startUpTorrents()
{
const QDir resumeDataDir {m_resumeFolderPath};
QStringList fastresumes = resumeDataDir.entryList(
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
const auto readFile = [](const QString &path, QByteArray &buf) -> bool
{
QFile file(path);
if (!file.open(QIODevice::ReadOnly))
{
LogMsg(tr("Cannot read file %1: %2").arg(path, file.errorString()), Log::WARNING);
return false;
}
buf = file.readAll();
return true;
};
qDebug("Starting up torrents...");
qDebug("Queue size: %d", fastresumes.size());
const QVector<TorrentID> torrents = startupStorage->registeredTorrents();
int resumedTorrentsCount = 0;
QVector<TorrentID> queue;
for (const TorrentID &torrentID : torrents)
const QRegularExpression rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
if (isQueueingSystemEnabled())
{
const std::optional<LoadTorrentParams> resumeData = startupStorage->load(torrentID);
if (resumeData)
QFile queueFile {resumeDataDir.absoluteFilePath(QLatin1String {"queue"})};
QStringList queue;
if (queueFile.open(QFile::ReadOnly))
{
if (m_resumeDataStorage != startupStorage)
{
m_resumeDataStorage->store(torrentID, *resumeData);
if (isQueueingSystemEnabled() && !resumeData->hasSeedStatus)
queue.append(torrentID);
}
QByteArray line;
while (!(line = queueFile.readLine()).isEmpty())
queue.append(QString::fromLatin1(line.trimmed()) + QLatin1String {".fastresume"});
}
else
{
LogMsg(tr("Couldn't load torrents queue from '%1'. Error: %2")
.arg(queueFile.fileName(), queueFile.errorString()), Log::WARNING);
}
qDebug() << "Starting up torrent" << torrentID.toString() << "...";
if (!loadTorrent(*resumeData))
if (!queue.empty())
fastresumes = queue + List::toSet(fastresumes).subtract(List::toSet(queue)).values();
}
int resumedTorrentsCount = 0;
for (const QString &fastresumeName : asConst(fastresumes))
{
const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
if (!rxMatch.hasMatch()) continue;
const QString hash = rxMatch.captured(1);
const QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
QByteArray data;
LoadTorrentParams torrentParams;
const QString torrentFilePath = resumeDataDir.filePath(QString::fromLatin1("%1.torrent").arg(hash));
TorrentInfo metadata = TorrentInfo::loadFromFile(torrentFilePath);
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, metadata, torrentParams))
{
qDebug() << "Starting up torrent" << hash << "...";
if (!loadTorrent(torrentParams))
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
.arg(torrentID.toString()), Log::CRITICAL);
.arg(hash), Log::CRITICAL);
// process add torrent messages before message queue overflow
if ((resumedTorrentsCount % 100) == 0) readAlerts();
@@ -4228,19 +4381,9 @@ void Session::startUpTorrents()
else
{
LogMsg(tr("Unable to resume torrent '%1'.", "e.g: Unable to resume torrent 'hash'.")
.arg(torrentID.toString()), Log::CRITICAL);
.arg(hash), Log::CRITICAL);
}
}
if (m_resumeDataStorage != startupStorage)
{
delete startupStorage;
if (resumeDataStorageType() == ResumeDataStorageType::Legacy)
Utils::Fs::forceRemove(dbPath);
if (isQueueingSystemEnabled())
m_resumeDataStorage->storeQueue(queue);
}
}
quint64 Session::getAlltimeDL() const
@@ -4322,9 +4465,6 @@ void Session::handleAlert(const lt::alert *a)
{
switch (a->type())
{
#if (LIBTORRENT_VERSION_NUM >= 20003)
case lt::file_prio_alert::alert_type:
#endif
case lt::file_renamed_alert::alert_type:
case lt::file_completed_alert::alert_type:
case lt::torrent_finished_alert::alert_type:
@@ -4424,15 +4564,9 @@ void Session::dispatchTorrentAlert(const lt::alert *a)
void Session::createTorrent(const lt::torrent_handle &nativeHandle)
{
#if (LIBTORRENT_VERSION_NUM >= 20000)
const auto torrentID = TorrentID::fromInfoHash(nativeHandle.info_hashes());
#else
const auto torrentID = TorrentID::fromInfoHash(nativeHandle.info_hash());
#endif
Q_ASSERT(m_loadingTorrents.contains(nativeHandle.info_hash()));
Q_ASSERT(m_loadingTorrents.contains(torrentID));
const LoadTorrentParams params = m_loadingTorrents.take(torrentID);
const LoadTorrentParams params = m_loadingTorrents.take(nativeHandle.info_hash());
auto *const torrent = new TorrentImpl {this, m_nativeSession, nativeHandle, params};
m_torrents.insert(torrent->id(), torrent);
@@ -4445,14 +4579,24 @@ void Session::createTorrent(const lt::torrent_handle &nativeHandle)
}
else
{
m_resumeDataStorage->store(torrent->id(), params);
// The following is useless for newly added magnet
if (hasMetadata)
{
// Copy the torrent file to the export folder
if (!torrentExportDirectory().isEmpty())
exportTorrentFile(torrent);
// Backup torrent file
const QDir resumeDataDir {m_resumeFolderPath};
const QString torrentFileName {QString::fromLatin1("%1.torrent").arg(torrent->id().toString())};
try
{
torrent->info().saveToFile(resumeDataDir.absoluteFilePath(torrentFileName));
// Copy the torrent file to the export folder
if (!torrentExportDirectory().isEmpty())
exportTorrentFile(torrent);
}
catch (const RuntimeError &err)
{
LogMsg(tr("Couldn't save torrent metadata file '%1'. Reason: %2")
.arg(torrentFileName, err.message()), Log::CRITICAL);
}
}
if (isAddTrackersEnabled() && !torrent->isPrivate())
@@ -4460,6 +4604,10 @@ void Session::createTorrent(const lt::torrent_handle &nativeHandle)
LogMsg(tr("'%1' added to download list.", "'torrent name' was added to download list.")
.arg(torrent->name()));
// In case of crash before the scheduled generation
// of the fastresumes.
torrent->saveResumeData();
}
if (((torrent->ratioLimit() >= 0) || (torrent->seedingTimeLimit() >= 0))
@@ -4481,18 +4629,10 @@ void Session::handleAddTorrentAlert(const lt::add_torrent_alert *p)
{
if (p->error)
{
const QString msg = QString::fromStdString(p->message());
LogMsg(tr("Couldn't load torrent. Reason: %1.").arg(msg), Log::WARNING);
qDebug("/!\\ Error: Failed to add torrent!");
QString msg = QString::fromStdString(p->message());
LogMsg(tr("Couldn't load torrent. Reason: %1").arg(msg), Log::WARNING);
emit loadTorrentFailed(msg);
const lt::add_torrent_params &params = p->params;
const bool hasMetadata = (params.ti && params.ti->is_valid());
#if (LIBTORRENT_VERSION_NUM >= 20000)
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);
#endif
m_loadingTorrents.remove(id);
}
else if (m_loadingTorrents.contains(p->handle.info_hash()))
{
@@ -4596,9 +4736,8 @@ void Session::handleFileErrorAlert(const lt::file_error_alert *p)
if (!torrent)
return;
torrent->handleAlert(p);
const TorrentID id = torrent->id();
if (!m_recentErroredTorrents.contains(id))
{
m_recentErroredTorrents.insert(id);
@@ -4689,7 +4828,8 @@ void Session::handleListenSucceededAlert(const lt::listen_succeeded_alert *p)
.arg(toString(p->address), proto, QString::number(p->port)), Log::INFO);
// Force reannounce on all torrents because some trackers blacklist some ports
reannounceToAllTrackers();
for (const lt::torrent_handle &torrent : m_nativeSession->get_torrents())
torrent.force_reannounce();
}
void Session::handleListenFailedAlert(const lt::listen_failed_alert *p)
@@ -4703,16 +4843,8 @@ void Session::handleListenFailedAlert(const lt::listen_failed_alert *p)
void Session::handleExternalIPAlert(const lt::external_ip_alert *p)
{
const QString externalIP {toString(p->external_address)};
LogMsg(tr("Detected external IP: %1", "e.g. Detected external IP: 1.1.1.1")
.arg(externalIP), Log::INFO);
if (m_lastExternalIP != externalIP)
{
if (isReannounceWhenAddressChangedEnabled() && !m_lastExternalIP.isEmpty())
reannounceToAllTrackers();
m_lastExternalIP = externalIP;
}
.arg(toString(p->external_address)), Log::INFO);
}
void Session::handleSessionStatsAlert(const lt::session_stats_alert *p)
@@ -4769,11 +4901,11 @@ void Session::handleSessionStatsAlert(const lt::session_stats_alert *p)
m_status.diskWriteQueue = stats[m_metricIndices.peer.numPeersDownDisk];
m_status.peersCount = stats[m_metricIndices.peer.numPeersConnected];
const int64_t numBlocksRead = stats[m_metricIndices.disk.numBlocksRead];
m_cacheStatus.totalUsedBuffers = stats[m_metricIndices.disk.diskBlocksInUse];
m_cacheStatus.jobQueueLength = stats[m_metricIndices.disk.queuedDiskJobs];
#if (LIBTORRENT_VERSION_NUM < 20000)
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);
#endif
@@ -4846,7 +4978,7 @@ void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert
void Session::handleStateUpdateAlert(const lt::state_update_alert *p)
{
QVector<Torrent *> updatedTorrents;
updatedTorrents.reserve(static_cast<decltype(updatedTorrents)::size_type>(p->status.size()));
updatedTorrents.reserve(p->status.size());
for (const lt::torrent_status &status : p->status)
{

View File

@@ -50,12 +50,10 @@
#include "cachestatus.h"
#include "sessionstatus.h"
#include "torrentinfo.h"
#include "trackerentry.h"
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
class QFile;
class QNetworkConfiguration;
class QNetworkConfigurationManager;
#endif
class QString;
class QThread;
class QTimer;
@@ -64,6 +62,7 @@ class QUrl;
class BandwidthScheduler;
class FileSearcher;
class FilterParserThread;
class ResumeDataSavingManager;
class Statistics;
// These values should remain unchanged when adding new items
@@ -97,11 +96,11 @@ namespace BitTorrent
{
class InfoHash;
class MagnetUri;
class ResumeDataStorage;
class Torrent;
class TorrentImpl;
class Tracker;
struct LoadTorrentParams;
struct TrackerEntry;
enum class MoveStorageMode;
@@ -142,13 +141,6 @@ namespace BitTorrent
};
Q_ENUM_NS(SeedChokingAlgorithm)
enum class ResumeDataStorageType
{
Legacy,
SQLite
};
Q_ENUM_NS(ResumeDataStorageType)
#if defined(Q_OS_WIN)
enum class OSMemoryPriority : int
{
@@ -372,8 +364,6 @@ namespace BitTorrent
void setSendBufferLowWatermark(int value);
int sendBufferWatermarkFactor() const;
void setSendBufferWatermarkFactor(int value);
int connectionSpeed() const;
void setConnectionSpeed(int value);
int socketBacklogSize() const;
void setSocketBacklogSize(int value);
bool isAnonymousModeEnabled() const;
@@ -404,9 +394,6 @@ namespace BitTorrent
void setAnnounceIP(const QString &ip);
int maxConcurrentHTTPAnnounces() const;
void setMaxConcurrentHTTPAnnounces(int value);
bool isReannounceWhenAddressChangedEnabled() const;
void setReannounceWhenAddressChangedEnabled(bool enabled);
void reannounceToAllTrackers() const;
int stopTrackerTimeout() const;
void setStopTrackerTimeout(int value);
int maxConnections() const;
@@ -441,8 +428,6 @@ namespace BitTorrent
void setTrackerFilteringEnabled(bool enabled);
QStringList bannedIPs() const;
void setBannedIPs(const QStringList &newList);
ResumeDataStorageType resumeDataStorageType() const;
void setResumeDataStorageType(ResumeDataStorageType type);
#if defined(Q_OS_WIN)
OSMemoryPriority getOSMemoryPriority() const;
void setOSMemoryPriority(OSMemoryPriority priority);
@@ -557,11 +542,9 @@ namespace BitTorrent
void handleDownloadFinished(const Net::DownloadResult &result);
void fileSearchFinished(const TorrentID &id, const QString &savePath, const QStringList &fileNames);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
// Session reconfiguration triggers
void networkOnlineStateChanged(bool online);
void networkConfigurationChange(const QNetworkConfiguration &);
#endif
private:
struct MoveStorageJob
@@ -584,6 +567,8 @@ namespace BitTorrent
bool hasPerTorrentRatioLimit() const;
bool hasPerTorrentSeedingTimeLimit() const;
void initResumeFolder();
// Session configuration
Q_INVOKABLE void configure();
void configureComponents();
@@ -608,6 +593,7 @@ namespace BitTorrent
void applyOSMemoryPriority() const;
#endif
bool loadTorrentResumeData(const QByteArray &data, const TorrentInfo &metadata, LoadTorrentParams &torrentParams);
bool loadTorrent(LoadTorrentParams params);
LoadTorrentParams initLoadTorrentParams(const AddTorrentParams &addTorrentParams);
bool addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source, const AddTorrentParams &addTorrentParams);
@@ -677,7 +663,6 @@ namespace BitTorrent
CachedSettingValue<int> m_sendBufferWatermark;
CachedSettingValue<int> m_sendBufferLowWatermark;
CachedSettingValue<int> m_sendBufferWatermarkFactor;
CachedSettingValue<int> m_connectionSpeed;
CachedSettingValue<int> m_socketBacklogSize;
CachedSettingValue<bool> m_isAnonymousModeEnabled;
CachedSettingValue<bool> m_isQueueingEnabled;
@@ -696,7 +681,6 @@ namespace BitTorrent
CachedSettingValue<bool> m_includeOverheadInLimits;
CachedSettingValue<QString> m_announceIP;
CachedSettingValue<int> m_maxConcurrentHTTPAnnounces;
CachedSettingValue<bool> m_isReannounceWhenAddressChangedEnabled;
CachedSettingValue<int> m_stopTrackerTimeout;
CachedSettingValue<int> m_maxConnections;
CachedSettingValue<int> m_maxUploads;
@@ -752,7 +736,6 @@ namespace BitTorrent
CachedSettingValue<int> m_peerTurnoverCutoff;
CachedSettingValue<int> m_peerTurnoverInterval;
CachedSettingValue<QStringList> m_bannedIPs;
CachedSettingValue<ResumeDataStorageType> m_resumeDataStorageType;
#if defined(Q_OS_WIN)
CachedSettingValue<OSMemoryPriority> m_OSMemoryPriority;
#endif
@@ -765,6 +748,8 @@ namespace BitTorrent
int m_numResumeData = 0;
int m_extraLimit = 0;
QVector<TrackerEntry> m_additionalTrackerList;
QString m_resumeFolderPath;
QFile *m_resumeFolderLock = nullptr;
bool m_refreshEnqueued = false;
QTimer *m_seedingLimitTimer = nullptr;
@@ -775,9 +760,9 @@ namespace BitTorrent
QPointer<BandwidthScheduler> m_bwScheduler;
// Tracker
QPointer<Tracker> m_tracker;
// fastresume data writing thread
QThread *m_ioThread = nullptr;
ResumeDataStorage *m_resumeDataStorage = nullptr;
ResumeDataSavingManager *m_resumeDataSavingManager = nullptr;
FileSearcher *m_fileSearcher = nullptr;
QSet<TorrentID> m_downloadedMetadata;
@@ -799,14 +784,11 @@ namespace BitTorrent
SessionStatus m_status;
CacheStatus m_cacheStatus;
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QNetworkConfigurationManager *m_networkManager = nullptr;
#endif
QList<MoveStorageJob> m_moveStorageQueue;
QString m_lastExternalIP;
static Session *m_instance;
};
}

View File

@@ -33,7 +33,6 @@
#include <QString>
#include <QtContainerFwd>
#include "base/tagset.h"
#include "abstractfilestorage.h"
class QBitArray;
@@ -50,21 +49,11 @@ namespace BitTorrent
struct PeerAddress;
struct TrackerEntry;
// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
// since `Q_NAMESPACE` cannot be used when the same namespace resides at different files.
// https://www.kdab.com/new-qt-5-8-meta-object-support-namespaces/#comment-143779
inline namespace TorrentOperatingModeNS
enum class TorrentOperatingMode
{
Q_NAMESPACE
enum class TorrentOperatingMode
{
AutoManaged = 0,
Forced = 1
};
Q_ENUM_NS(TorrentOperatingMode)
}
AutoManaged = 0,
Forced = 1
};
enum class TorrentState
{
@@ -95,6 +84,12 @@ namespace BitTorrent
Error
};
struct TrackerInfo
{
QString lastMessage;
int numPeers = 0;
};
uint qHash(TorrentState key, uint seed);
class Torrent : public AbstractFileStorage
@@ -179,7 +174,7 @@ namespace BitTorrent
virtual bool belongsToCategory(const QString &category) const = 0;
virtual bool setCategory(const QString &category) = 0;
virtual TagSet tags() const = 0;
virtual QSet<QString> tags() const = 0;
virtual bool hasTag(const QString &tag) const = 0;
virtual bool addTag(const QString &tag) = 0;
virtual bool removeTag(const QString &tag) = 0;
@@ -216,6 +211,7 @@ namespace BitTorrent
virtual bool hasFilteredPieces() const = 0;
virtual int queuePosition() const = 0;
virtual QVector<TrackerEntry> trackers() const = 0;
virtual QHash<QString, TrackerInfo> trackerInfos() const = 0;
virtual QVector<QUrl> urlSeeds() const = 0;
virtual QString error() const = 0;
virtual qlonglong totalDownload() const = 0;

View File

@@ -42,9 +42,9 @@
#include "base/exceptions.h"
#include "base/global.h"
#include "base/utils/compare.h"
#include "base/utils/fs.h"
#include "base/utils/io.h"
#include "base/utils/string.h"
#include "base/version.h"
#include "ltunderlyingtype.h"
@@ -98,12 +98,6 @@ void TorrentCreatorThread::sendProgressSignal(int currentPieceIdx, int totalPiec
emit updateProgress(static_cast<int>((currentPieceIdx * 100.) / totalPieces));
}
void TorrentCreatorThread::checkInterruptionRequested() const
{
if (isInterruptionRequested())
throw RuntimeError(tr("Operation aborted"));
}
void TorrentCreatorThread::run()
{
emit updateProgress(0);
@@ -111,7 +105,6 @@ void TorrentCreatorThread::run()
try
{
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + '/';
const Utils::Compare::NaturalLessThan<Qt::CaseInsensitive> naturalLessThan {};
// Adding files to the torrent
lt::file_storage fs;
@@ -124,13 +117,13 @@ void TorrentCreatorThread::run()
// need to sort the file names by natural sort order
QStringList dirs = {m_params.inputPath};
QDirIterator dirIter {m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
QDirIterator dirIter(m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories);
while (dirIter.hasNext())
{
dirIter.next();
dirs += dirIter.filePath();
}
std::sort(dirs.begin(), dirs.end(), naturalLessThan);
std::sort(dirs.begin(), dirs.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
QStringList fileNames;
QHash<QString, qint64> fileSizeMap;
@@ -149,7 +142,7 @@ void TorrentCreatorThread::run()
fileSizeMap[relFilePath] = fileIter.fileInfo().size();
}
std::sort(tmpNames.begin(), tmpNames.end(), naturalLessThan);
std::sort(tmpNames.begin(), tmpNames.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
fileNames += tmpNames;
}
@@ -157,7 +150,7 @@ void TorrentCreatorThread::run()
fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
}
checkInterruptionRequested();
if (isInterruptionRequested()) return;
#if (LIBTORRENT_VERSION_NUM >= 20000)
lt::create_torrent newTorrent {fs, m_params.pieceSize, toNativeTorrentFormatFlag(m_params.torrentFormat)};
@@ -187,7 +180,9 @@ void TorrentCreatorThread::run()
lt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
, [this, &newTorrent](const lt::piece_index_t n)
{
checkInterruptionRequested();
if (isInterruptionRequested())
throw RuntimeError {tr("Create new torrent aborted.")};
sendProgressSignal(static_cast<LTUnderlyingType<lt::piece_index_t>>(n), newTorrent.num_pieces());
});
@@ -198,7 +193,7 @@ void TorrentCreatorThread::run()
// Is private ?
newTorrent.set_priv(m_params.isPrivate);
checkInterruptionRequested();
if (isInterruptionRequested()) return;
lt::entry entry = newTorrent.generate();
@@ -206,30 +201,32 @@ void TorrentCreatorThread::run()
if (!m_params.source.isEmpty())
entry["info"]["source"] = m_params.source.toStdString();
checkInterruptionRequested();
if (isInterruptionRequested()) return;
// create the torrent
QFile outfile {m_params.savePath};
if (!outfile.open(QIODevice::WriteOnly))
throw RuntimeError(outfile.errorString());
{
throw RuntimeError {tr("Create new torrent file failed. Reason: %1")
.arg(outfile.errorString())};
}
checkInterruptionRequested();
if (isInterruptionRequested()) return;
lt::bencode(Utils::IO::FileDeviceOutputIterator {outfile}, entry);
if (outfile.error() != QFileDevice::NoError)
throw RuntimeError(outfile.errorString());
{
throw RuntimeError {tr("Create new torrent file failed. Reason: %1")
.arg(outfile.errorString())};
}
outfile.close();
emit updateProgress(100);
emit creationSuccess(m_params.savePath, parentPath);
}
catch (const RuntimeError &err)
catch (const std::exception &e)
{
emit creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(err.message()));
}
catch (const std::exception &err)
{
emit creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(QString::fromLocal8Bit(err.what())));
emit creationFailure(e.what());
}
}

View File

@@ -65,11 +65,10 @@ namespace BitTorrent
class TorrentCreatorThread final : public QThread
{
Q_OBJECT
Q_DISABLE_COPY_MOVE(TorrentCreatorThread)
public:
explicit TorrentCreatorThread(QObject *parent = nullptr);
~TorrentCreatorThread() override;
TorrentCreatorThread(QObject *parent = nullptr);
~TorrentCreatorThread();
void create(const TorrentCreatorParams &params);
@@ -80,15 +79,16 @@ namespace BitTorrent
, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit);
#endif
protected:
void run() override;
signals:
void creationFailure(const QString &msg);
void creationSuccess(const QString &path, const QString &branchPath);
void updateProgress(int progress);
private:
void run() override;
void sendProgressSignal(int currentPieceIdx, int totalPieces);
void checkInterruptionRequested() const;
TorrentCreatorParams m_params;
};

View File

@@ -63,7 +63,6 @@
#include "base/utils/string.h"
#include "common.h"
#include "downloadpriority.h"
#include "loadtorrentparams.h"
#include "ltqhash.h"
#include "ltunderlyingtype.h"
#include "peeraddress.h"
@@ -97,11 +96,9 @@ namespace
}
#if (LIBTORRENT_VERSION_NUM >= 20000)
TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry
, const lt::info_hash_t &hashes, const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry, const lt::info_hash_t &hashes)
#else
TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry
, const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry)
#endif
{
TrackerEntry trackerEntry {QString::fromStdString(nativeEntry.url), nativeEntry.tier};
@@ -109,11 +106,9 @@ namespace
int numUpdating = 0;
int numWorking = 0;
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);
trackerEntry.endpoints.reserve(static_cast<decltype(trackerEntry.endpoints)::size_type>(numEndpoints));
const int numEndpoints = nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1);
trackerEntry.endpoints.reserve(numEndpoints);
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
{
for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2})
@@ -124,11 +119,9 @@ namespace
TrackerEntry::EndpointStats trackerEndpoint;
trackerEndpoint.protocolVersion = (protocolVersion == lt::protocol_version::V1) ? 1 : 2;
trackerEndpoint.numPeers = trackerPeerCounts.value(endpoint.local_endpoint, -1);
trackerEndpoint.numSeeds = infoHash.scrape_complete;
trackerEndpoint.numLeeches = infoHash.scrape_incomplete;
trackerEndpoint.numDownloaded = infoHash.scrape_downloaded;
if (infoHash.updating)
{
trackerEndpoint.status = TrackerEntry::Updating;
@@ -148,21 +141,11 @@ namespace
{
trackerEndpoint.status = TrackerEntry::NotContacted;
}
const QString trackerMessage = QString::fromStdString(infoHash.message);
const QString errorMessage = QString::fromLocal8Bit(infoHash.last_error.message().c_str());
trackerEndpoint.message = (!trackerMessage.isEmpty() ? trackerMessage : errorMessage);
trackerEntry.endpoints.append(trackerEndpoint);
trackerEntry.numPeers = std::max(trackerEntry.numPeers, trackerEndpoint.numPeers);
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, trackerEndpoint.numSeeds);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, trackerEndpoint.numLeeches);
trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, trackerEndpoint.numDownloaded);
if (firstTrackerMessage.isEmpty())
firstTrackerMessage = trackerMessage;
if (firstErrorMessage.isEmpty())
firstErrorMessage = errorMessage;
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, infoHash.scrape_complete);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, infoHash.scrape_incomplete);
trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, infoHash.scrape_downloaded);
}
}
}
@@ -172,11 +155,9 @@ namespace
for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
{
TrackerEntry::EndpointStats trackerEndpoint;
trackerEndpoint.numPeers = trackerPeerCounts.value(endpoint.local_endpoint, -1);
trackerEndpoint.numSeeds = endpoint.scrape_complete;
trackerEndpoint.numLeeches = endpoint.scrape_incomplete;
trackerEndpoint.numDownloaded = endpoint.scrape_downloaded;
if (endpoint.updating)
{
trackerEndpoint.status = TrackerEntry::Updating;
@@ -196,40 +177,22 @@ namespace
{
trackerEndpoint.status = TrackerEntry::NotContacted;
}
const QString trackerMessage = QString::fromStdString(endpoint.message);
const QString errorMessage = QString::fromLocal8Bit(endpoint.last_error.message().c_str());
trackerEndpoint.message = (!trackerMessage.isEmpty() ? trackerMessage : errorMessage);
trackerEntry.endpoints.append(trackerEndpoint);
trackerEntry.numPeers = std::max(trackerEntry.numPeers, trackerEndpoint.numPeers);
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, trackerEndpoint.numSeeds);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, trackerEndpoint.numLeeches);
trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, trackerEndpoint.numDownloaded);
if (firstTrackerMessage.isEmpty())
firstTrackerMessage = trackerMessage;
if (firstErrorMessage.isEmpty())
firstErrorMessage = errorMessage;
trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, endpoint.scrape_complete);
trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, endpoint.scrape_incomplete);
trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, endpoint.scrape_downloaded);
}
#endif
if (numEndpoints > 0)
{
if (numUpdating > 0)
{
trackerEntry.status = TrackerEntry::Updating;
}
else if (numWorking > 0)
{
trackerEntry.status = TrackerEntry::Working;
trackerEntry.message = firstTrackerMessage;
}
else if (numNotWorking == numEndpoints)
{
trackerEntry.status = TrackerEntry::NotWorking;
trackerEntry.message = (!firstTrackerMessage.isEmpty() ? firstTrackerMessage : firstErrorMessage);
}
}
return trackerEntry;
@@ -276,12 +239,12 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
, m_tags(params.tags)
, m_ratioLimit(params.ratioLimit)
, m_seedingTimeLimit(params.seedingTimeLimit)
, m_operatingMode(params.operatingMode)
, m_operatingMode(params.forced ? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged)
, m_contentLayout(params.contentLayout)
, m_hasSeedStatus(params.hasSeedStatus)
, m_hasFirstLastPiecePriority(params.firstLastPiecePriority)
, m_useAutoTMM(params.savePath.isEmpty())
, m_isStopped(params.stopped)
, m_isStopped(params.paused)
, m_ltAddTorrentParams(params.ltAddTorrentParams)
{
if (m_useAutoTMM)
@@ -473,21 +436,23 @@ QVector<TrackerEntry> TorrentImpl::trackers() const
const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers();
QVector<TrackerEntry> entries;
entries.reserve(static_cast<decltype(entries)::size_type>(nativeTrackers.size()));
entries.reserve(nativeTrackers.size());
for (const lt::announce_entry &tracker : nativeTrackers)
{
const QString trackerURL = QString::fromStdString(tracker.url);
#if (LIBTORRENT_VERSION_NUM >= 20000)
entries << fromNativeAnnouncerEntry(tracker, m_nativeHandle.info_hashes(), m_trackerPeerCounts[trackerURL]);
entries << fromNativeAnnouncerEntry(tracker, m_nativeHandle.info_hashes());
#else
entries << fromNativeAnnouncerEntry(tracker, m_trackerPeerCounts[trackerURL]);
entries << fromNativeAnnouncerEntry(tracker);
#endif
}
return entries;
}
QHash<QString, TrackerInfo> TorrentImpl::trackerInfos() const
{
return m_trackerInfos;
}
void TorrentImpl::addTrackers(const QVector<TrackerEntry> &trackers)
{
QSet<TrackerEntry> currentTrackers;
@@ -560,10 +525,10 @@ QVector<QUrl> TorrentImpl::urlSeeds() const
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
QVector<QUrl> urlSeeds;
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(currentSeeds.size()));
urlSeeds.reserve(currentSeeds.size());
for (const std::string &urlSeed : currentSeeds)
urlSeeds.append(QString::fromStdString(urlSeed));
urlSeeds.append(QUrl(urlSeed.c_str()));
return urlSeeds;
}
@@ -703,7 +668,7 @@ bool TorrentImpl::belongsToCategory(const QString &category) const
return false;
}
TagSet TorrentImpl::tags() const
QSet<QString> TorrentImpl::tags() const
{
return m_tags;
}
@@ -717,18 +682,18 @@ bool TorrentImpl::addTag(const QString &tag)
{
if (!Session::isValidTag(tag))
return false;
if (hasTag(tag))
return false;
if (!m_session->hasTag(tag))
if (!hasTag(tag))
{
if (!m_session->addTag(tag))
return false;
if (!m_session->hasTag(tag))
if (!m_session->addTag(tag))
return false;
m_tags.insert(tag);
m_session->handleTorrentNeedSaveResumeData(this);
m_session->handleTorrentTagAdded(this, tag);
return true;
}
m_tags.insert(tag);
m_session->handleTorrentNeedSaveResumeData(this);
m_session->handleTorrentTagAdded(this, tag);
return true;
return false;
}
bool TorrentImpl::removeTag(const QString &tag)
@@ -1005,13 +970,7 @@ QString TorrentImpl::error() const
return QString::fromStdString(m_nativeStatus.errc.message());
if (m_nativeStatus.flags & lt::torrent_flags::upload_mode)
{
const QString writeErrorStr = tr("Couldn't write to file.");
const QString uploadModeStr = tr("Torrent is currently in \"upload only\" mode.");
const QString errorMessage = QString::fromLocal8Bit(m_lastFileError.error.message().c_str());
return writeErrorStr + QLatin1Char(' ') + errorMessage + QLatin1String(". ") + uploadModeStr;
}
return tr("There's not enough space on disk. Torrent is currently in \"upload only\" mode.");
return {};
}
@@ -1223,11 +1182,9 @@ QVector<PeerInfo> TorrentImpl::peers() const
m_nativeHandle.get_peer_info(nativePeers);
QVector<PeerInfo> peers;
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
peers.reserve(nativePeers.size());
for (const lt::peer_info &peer : nativePeers)
peers << PeerInfo(this, peer);
return peers;
}
@@ -1260,7 +1217,7 @@ QVector<int> TorrentImpl::pieceAvailability() const
std::vector<int> avail;
m_nativeHandle.piece_availability(avail);
return {avail.cbegin(), avail.cend()};
return Vector::fromStdVector(avail);
}
qreal TorrentImpl::distributedCopies() const
@@ -1518,7 +1475,6 @@ void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QSt
m_maintenanceJob = MaintenanceJob::None;
updateStatus();
prepareResumeData(p);
m_session->handleTorrentMetadataReceived(this);
}
@@ -1665,8 +1621,10 @@ void TorrentImpl::handleMoveStorageJobFinished(const bool hasOutstandingJob)
void TorrentImpl::handleTrackerReplyAlert(const lt::tracker_reply_alert *p)
{
const QString trackerUrl = p->tracker_url();
m_trackerPeerCounts[trackerUrl][p->local_endpoint] = p->num_peers;
const QString trackerUrl(p->tracker_url());
qDebug("Received a tracker reply from %s (Num_peers = %d)", qUtf8Printable(trackerUrl), p->num_peers);
// Connection was successful now. Remove possible old errors
m_trackerInfos[trackerUrl] = {{}, p->num_peers};
m_session->handleTorrentTrackerReply(this, trackerUrl);
}
@@ -1674,12 +1632,20 @@ void TorrentImpl::handleTrackerReplyAlert(const lt::tracker_reply_alert *p)
void TorrentImpl::handleTrackerWarningAlert(const lt::tracker_warning_alert *p)
{
const QString trackerUrl = p->tracker_url();
const QString message = p->warning_message();
// Connection was successful now but there is a warning message
m_trackerInfos[trackerUrl].lastMessage = message; // Store warning message
m_session->handleTorrentTrackerWarning(this, trackerUrl);
}
void TorrentImpl::handleTrackerErrorAlert(const lt::tracker_error_alert *p)
{
const QString trackerUrl = p->tracker_url();
const QString message = p->error_message();
m_trackerInfos[trackerUrl].lastMessage = message;
// Starting with libtorrent 1.2.x each tracker has multiple local endpoints from which
// an announce is attempted. Some endpoints might succeed while others might fail.
@@ -1771,10 +1737,31 @@ void TorrentImpl::handleTorrentResumedAlert(const lt::torrent_resumed_alert *p)
void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
{
if (m_hasMissingFiles)
{
const auto havePieces = m_ltAddTorrentParams.have_pieces;
const auto unfinishedPieces = m_ltAddTorrentParams.unfinished_pieces;
const auto verifiedPieces = m_ltAddTorrentParams.verified_pieces;
// Update recent resume data but preserve existing progress
m_ltAddTorrentParams = p->params;
m_ltAddTorrentParams.have_pieces = havePieces;
m_ltAddTorrentParams.unfinished_pieces = unfinishedPieces;
m_ltAddTorrentParams.verified_pieces = verifiedPieces;
}
else
{
// Update recent resume data
m_ltAddTorrentParams = p->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;
if (m_maintenanceJob == MaintenanceJob::HandleMetadata)
{
m_ltAddTorrentParams = p->params;
m_ltAddTorrentParams.have_pieces.clear();
m_ltAddTorrentParams.verified_pieces.clear();
@@ -1783,36 +1770,6 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
m_session->findIncompleteFiles(metadata, m_savePath);
}
else
{
prepareResumeData(p->params);
}
}
void TorrentImpl::prepareResumeData(const lt::add_torrent_params &params)
{
if (m_hasMissingFiles)
{
const auto havePieces = m_ltAddTorrentParams.have_pieces;
const auto unfinishedPieces = m_ltAddTorrentParams.unfinished_pieces;
const auto verifiedPieces = m_ltAddTorrentParams.verified_pieces;
// Update recent resume data but preserve existing progress
m_ltAddTorrentParams = params;
m_ltAddTorrentParams.have_pieces = havePieces;
m_ltAddTorrentParams.unfinished_pieces = unfinishedPieces;
m_ltAddTorrentParams.verified_pieces = verifiedPieces;
}
else
{
// Update recent resume data
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;
LoadTorrentParams resumeData;
resumeData.name = m_name;
@@ -1824,8 +1781,8 @@ void TorrentImpl::prepareResumeData(const lt::add_torrent_params &params)
resumeData.seedingTimeLimit = m_seedingTimeLimit;
resumeData.firstLastPiecePriority = m_hasFirstLastPiecePriority;
resumeData.hasSeedStatus = m_hasSeedStatus;
resumeData.stopped = m_isStopped;
resumeData.operatingMode = m_operatingMode;
resumeData.paused = m_isStopped;
resumeData.forced = (m_operatingMode == TorrentOperatingMode::Forced);
resumeData.ltAddTorrentParams = m_ltAddTorrentParams;
m_session->handleTorrentResumeDataReady(this, resumeData);
@@ -1865,9 +1822,9 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
if (m_oldPath[p->index].isEmpty())
m_oldPath.remove(p->index);
QVector<QStringRef> oldPathParts = oldFilePath.splitRef('/', Qt::SkipEmptyParts);
QVector<QStringRef> oldPathParts = oldFilePath.splitRef('/', QString::SkipEmptyParts);
oldPathParts.removeLast(); // drop file name part
QVector<QStringRef> newPathParts = newFilePath.splitRef('/', Qt::SkipEmptyParts);
QVector<QStringRef> newPathParts = newFilePath.splitRef('/', QString::SkipEmptyParts);
newPathParts.removeLast(); // drop file name part
#if defined(Q_OS_WIN)
@@ -1930,19 +1887,6 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
}
}
void TorrentImpl::handleFileErrorAlert(const lt::file_error_alert *p)
{
m_lastFileError = {p->error, p->op};
}
#if (LIBTORRENT_VERSION_NUM >= 20003)
void TorrentImpl::handleFilePrioAlert(const lt::file_prio_alert *)
{
if (m_nativeHandle.need_save_resume_data())
m_session->handleTorrentNeedSaveResumeData(this);
}
#endif
void TorrentImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
{
Q_UNUSED(p);
@@ -1980,11 +1924,6 @@ void TorrentImpl::handleAlert(const lt::alert *a)
{
switch (a->type())
{
#if (LIBTORRENT_VERSION_NUM >= 20003)
case lt::file_prio_alert::alert_type:
handleFilePrioAlert(static_cast<const lt::file_prio_alert*>(a));
break;
#endif
case lt::file_renamed_alert::alert_type:
handleFileRenamedAlert(static_cast<const lt::file_renamed_alert*>(a));
break;
@@ -1994,9 +1933,6 @@ void TorrentImpl::handleAlert(const lt::alert *a)
case lt::file_completed_alert::alert_type:
handleFileCompletedAlert(static_cast<const lt::file_completed_alert*>(a));
break;
case lt::file_error_alert::alert_type:
handleFileErrorAlert(static_cast<const lt::file_error_alert*>(a));
break;
case lt::torrent_finished_alert::alert_type:
handleTorrentFinishedAlert(static_cast<const lt::torrent_finished_alert*>(a));
break;

View File

@@ -33,38 +33,46 @@
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/fwd.hpp>
#include <libtorrent/socket.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_status.hpp>
#include <QDateTime>
#include <QHash>
#include <QMap>
#include <QObject>
#include <QQueue>
#include <QSet>
#include <QString>
#include <QVector>
#include "base/tagset.h"
#include "infohash.h"
#include "speedmonitor.h"
#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;
struct LoadTorrentParams;
struct AddTorrentParams;
struct LoadTorrentParams
{
lt::add_torrent_params ltAddTorrentParams {};
QString name;
QString category;
QSet<QString> tags;
QString savePath;
TorrentContentLayout contentLayout = TorrentContentLayout::Original;
bool firstLastPiecePriority = false;
bool hasSeedStatus = false;
bool forced = false;
bool paused = false;
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
bool restored = false; // is existing torrent job?
};
enum class MoveStorageMode
{
@@ -78,12 +86,6 @@ namespace BitTorrent
HandleMetadata
};
struct FileErrorInfo
{
lt::error_code error;
lt::operation_t operation;
};
class TorrentImpl final : public QObject, public Torrent
{
Q_DISABLE_COPY(TorrentImpl)
@@ -121,7 +123,7 @@ namespace BitTorrent
bool belongsToCategory(const QString &category) const override;
bool setCategory(const QString &category) override;
TagSet tags() const override;
QSet<QString> tags() const override;
bool hasTag(const QString &tag) const override;
bool addTag(const QString &tag) override;
bool removeTag(const QString &tag) override;
@@ -162,6 +164,7 @@ namespace BitTorrent
bool hasFilteredPieces() const override;
int queuePosition() const override;
QVector<TrackerEntry> trackers() const override;
QHash<QString, TrackerInfo> trackerInfos() const override;
QVector<QUrl> urlSeeds() const override;
QString error() const override;
qlonglong totalDownload() const override;
@@ -253,7 +256,7 @@ namespace BitTorrent
QString actualStorageLocation() const;
private:
using EventTrigger = std::function<void ()>;
typedef std::function<void ()> EventTrigger;
void updateStatus();
void updateStatus(const lt::torrent_status &nativeStatus);
@@ -261,10 +264,6 @@ 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)
void handleFilePrioAlert(const lt::file_prio_alert *p);
#endif
void handleFileRenamedAlert(const lt::file_renamed_alert *p);
void handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p);
void handleMetadataReceivedAlert(const lt::metadata_received_alert *p);
@@ -290,7 +289,6 @@ namespace BitTorrent
void manageIncompleteFiles();
void applyFirstLastPiecePriority(bool enabled, const QVector<DownloadPriority> &updatedFilePrio = {});
void prepareResumeData(const lt::add_torrent_params &params);
void endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames);
void reload();
@@ -316,14 +314,13 @@ namespace BitTorrent
// we will rely on this workaround to remove empty leftover folders
QHash<lt::file_index_t, QVector<QString>> m_oldPath;
QHash<QString, QMap<lt::tcp::endpoint, int>> m_trackerPeerCounts;
FileErrorInfo m_lastFileError;
QHash<QString, TrackerInfo> m_trackerInfos;
// Persistent data
QString m_name;
QString m_savePath;
QString m_category;
TagSet m_tags;
QSet<QString> m_tags;
qreal m_ratioLimit;
int m_seedingTimeLimit;
TorrentOperatingMode m_operatingMode;

View File

@@ -75,8 +75,6 @@ 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);
@@ -167,25 +165,18 @@ TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexc
void TorrentInfo::saveToFile(const QString &path) const
{
if (!isValid())
throw RuntimeError {tr("Invalid metadata")};
throw RuntimeError {tr("Invalid metadata.")};
try
{
const auto torrentCreator = lt::create_torrent(*nativeInfo());
const lt::entry torrentEntry = torrentCreator.generate();
const lt::create_torrent torrentCreator = lt::create_torrent(*(nativeInfo()));
const lt::entry torrentEntry = torrentCreator.generate();
QFile torrentFile {path};
if (!torrentFile.open(QIODevice::WriteOnly))
throw RuntimeError(torrentFile.errorString());
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());
}
catch (const lt::system_error &err)
{
throw RuntimeError(QString::fromLocal8Bit(err.what()));
}
lt::bencode(Utils::IO::FileDeviceOutputIterator {torrentFile}, torrentEntry);
if (torrentFile.error() != QFileDevice::NoError)
throw RuntimeError {torrentFile.errorString()};
}
bool TorrentInfo::isValid() const
@@ -313,11 +304,10 @@ QVector<TrackerEntry> TorrentInfo::trackers() const
const std::vector<lt::announce_entry> trackers = m_nativeInfo->trackers();
QVector<TrackerEntry> ret;
ret.reserve(static_cast<decltype(ret)::size_type>(trackers.size()));
ret.reserve(trackers.size());
for (const lt::announce_entry &tracker : trackers)
ret.append({QString::fromStdString(tracker.url)});
return ret;
}
@@ -328,7 +318,7 @@ QVector<QUrl> TorrentInfo::urlSeeds() const
const std::vector<lt::web_seed_entry> &nativeWebSeeds = m_nativeInfo->web_seeds();
QVector<QUrl> urlSeeds;
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(nativeWebSeeds.size()));
urlSeeds.reserve(nativeWebSeeds.size());
for (const lt::web_seed_entry &webSeed : nativeWebSeeds)
{
@@ -368,10 +358,11 @@ QVector<int> TorrentInfo::fileIndicesForPiece(const int pieceIndex) const
if (!isValid() || (pieceIndex < 0) || (pieceIndex >= piecesCount()))
return {};
const std::vector<lt::file_slice> files = nativeInfo()->map_block(
lt::piece_index_t {pieceIndex}, 0, nativeInfo()->piece_size(lt::piece_index_t {pieceIndex}));
const std::vector<lt::file_slice> files(
nativeInfo()->map_block(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()));
res.reserve(int(files.size()));
std::transform(files.begin(), files.end(), std::back_inserter(res),
[](const lt::file_slice &s) { return static_cast<int>(s.file_index); });

View File

@@ -110,5 +110,3 @@ namespace BitTorrent
std::shared_ptr<lt::torrent_info> m_nativeInfo;
};
}
Q_DECLARE_METATYPE(BitTorrent::TorrentInfo)

View File

@@ -265,7 +265,7 @@ Http::Response Tracker::processRequest(const Http::Request &request, const Http:
const lt::entry::dictionary_type bencodedEntry =
{
{ANNOUNCE_RESPONSE_FAILURE_REASON, {error.message().toStdString()}}
{ANNOUNCE_RESPONSE_FAILURE_REASON, {error.what()}}
};
QByteArray reply;
lt::bencode(std::back_inserter(reply), bencodedEntry);

View File

@@ -49,25 +49,21 @@ namespace BitTorrent
int protocolVersion = 1;
Status status = NotContacted;
int numPeers = -1;
int numSeeds = -1;
int numLeeches = -1;
int numDownloaded = -1;
QString message {};
};
QString url {};
QString url;
int tier = 0;
QVector<EndpointStats> endpoints {};
// Deprecated fields
Status status = NotContacted;
int numPeers = -1;
int numSeeds = -1;
int numLeeches = -1;
int numDownloaded = -1;
QString message {};
};
bool operator==(const TrackerEntry &left, const TrackerEntry &right);

View File

@@ -28,12 +28,12 @@
#include "exceptions.h"
Exception::Exception(const QString &message) noexcept
: m_message {message}
RuntimeError::RuntimeError(const QString &message)
: std::runtime_error {message.toUtf8().data()}
{
}
QString Exception::message() const noexcept
QString RuntimeError::message() const
{
return m_message;
return what();
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018, 2021 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018 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,26 +28,12 @@
#pragma once
#include <stdexcept>
#include <QString>
class Exception
class RuntimeError : public std::runtime_error
{
public:
explicit Exception(const QString &message = {}) noexcept;
[[nodiscard]] QString message() const noexcept;
private:
QString m_message;
};
class RuntimeError : public Exception
{
public:
using Exception::Exception;
};
class InvalidArgument : public Exception
{
public:
using Exception::Exception;
explicit RuntimeError(const QString &message = {});
QString message() const;
};

View File

@@ -0,0 +1,187 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "filesystemwatcher.h"
#include <QtGlobal>
#include <chrono>
#if defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
#include <cstring>
#include <sys/mount.h>
#include <sys/param.h>
#endif
#include "base/algorithm.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/global.h"
#include "base/logger.h"
#include "base/utils/fs.h"
using namespace std::chrono_literals;
namespace
{
const std::chrono::duration WATCH_INTERVAL = 10s;
const int MAX_PARTIAL_RETRIES = 5;
}
FileSystemWatcher::FileSystemWatcher(QObject *parent)
: QFileSystemWatcher(parent)
{
connect(this, &QFileSystemWatcher::directoryChanged, this, &FileSystemWatcher::scanLocalFolder);
m_partialTorrentTimer.setSingleShot(true);
connect(&m_partialTorrentTimer, &QTimer::timeout, this, &FileSystemWatcher::processPartialTorrents);
connect(&m_watchTimer, &QTimer::timeout, this, &FileSystemWatcher::scanNetworkFolders);
}
QStringList FileSystemWatcher::directories() const
{
QStringList dirs = QFileSystemWatcher::directories();
for (const QDir &dir : asConst(m_watchedFolders))
dirs << dir.canonicalPath();
return dirs;
}
void FileSystemWatcher::addPath(const QString &path)
{
if (path.isEmpty()) return;
#if !defined Q_OS_HAIKU
const QDir dir(path);
if (!dir.exists()) return;
// Check if the path points to a network file system or not
if (Utils::Fs::isNetworkFileSystem(path))
{
// Network mode
LogMsg(tr("Watching remote folder: \"%1\"").arg(Utils::Fs::toNativePath(path)));
m_watchedFolders << dir;
m_watchTimer.start(WATCH_INTERVAL);
return;
}
#endif
// Normal mode
LogMsg(tr("Watching local folder: \"%1\"").arg(Utils::Fs::toNativePath(path)));
QFileSystemWatcher::addPath(path);
scanLocalFolder(path);
}
void FileSystemWatcher::removePath(const QString &path)
{
if (m_watchedFolders.removeOne(path))
{
if (m_watchedFolders.isEmpty())
m_watchTimer.stop();
return;
}
// Normal mode
QFileSystemWatcher::removePath(path);
}
void FileSystemWatcher::scanLocalFolder(const QString &path)
{
QTimer::singleShot(2000, this, [this, path]() { processTorrentsInDir(path); });
}
void FileSystemWatcher::scanNetworkFolders()
{
for (const QDir &dir : asConst(m_watchedFolders))
processTorrentsInDir(dir);
}
void FileSystemWatcher::processPartialTorrents()
{
QStringList noLongerPartial;
// Check which torrents are still partial
Algorithm::removeIf(m_partialTorrents, [&noLongerPartial](const QString &torrentPath, int &value)
{
if (!QFile::exists(torrentPath))
return true;
if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid())
{
noLongerPartial << torrentPath;
return true;
}
if (value >= MAX_PARTIAL_RETRIES)
{
QFile::rename(torrentPath, torrentPath + ".qbt_rejected");
return true;
}
++value;
return false;
});
// Stop the partial timer if necessary
if (m_partialTorrents.empty())
{
m_partialTorrentTimer.stop();
qDebug("No longer any partial torrent.");
}
else
{
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
m_partialTorrentTimer.start(WATCH_INTERVAL);
}
// Notify of new torrents
if (!noLongerPartial.isEmpty())
emit torrentsAdded(noLongerPartial);
}
void FileSystemWatcher::processTorrentsInDir(const QDir &dir)
{
QStringList torrents;
const QStringList files = dir.entryList({"*.torrent", "*.magnet"}, QDir::Files);
for (const QString &file : files)
{
const QString fileAbsPath = dir.absoluteFilePath(file);
if (file.endsWith(".magnet", Qt::CaseInsensitive))
torrents << fileAbsPath;
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid())
torrents << fileAbsPath;
else if (!m_partialTorrents.contains(fileAbsPath))
m_partialTorrents[fileAbsPath] = 0;
}
if (!torrents.empty())
emit torrentsAdded(torrents);
if (!m_partialTorrents.empty() && !m_partialTorrentTimer.isActive())
m_partialTorrentTimer.start(WATCH_INTERVAL);
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2021 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -28,33 +28,43 @@
#pragma once
#include <libtorrent/add_torrent_params.hpp>
#include <QDir>
#include <QFileSystemWatcher>
#include <QHash>
#include <QTimer>
#include <QVector>
#include <QString>
#include "base/tagset.h"
#include "torrent.h"
#include "torrentcontentlayout.h"
namespace BitTorrent
/*
* Subclassing QFileSystemWatcher in order to support Network File
* System watching (NFS, CIFS) on Linux and Mac OS.
*/
class FileSystemWatcher final : public QFileSystemWatcher
{
struct LoadTorrentParams
{
lt::add_torrent_params ltAddTorrentParams {};
Q_OBJECT
Q_DISABLE_COPY(FileSystemWatcher)
QString name;
QString category;
TagSet tags;
QString savePath;
TorrentContentLayout contentLayout = TorrentContentLayout::Original;
TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged;
bool firstLastPiecePriority = false;
bool hasSeedStatus = false;
bool stopped = false;
public:
explicit FileSystemWatcher(QObject *parent = nullptr);
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
QStringList directories() const;
void addPath(const QString &path);
void removePath(const QString &path);
bool restored = false; // is existing torrent job?
};
}
signals:
void torrentsAdded(const QStringList &pathList);
private slots:
void scanLocalFolder(const QString &path);
void processPartialTorrents();
void scanNetworkFolders();
private:
void processTorrentsInDir(const QDir &dir);
// Partial torrents
QHash<QString, int> m_partialTorrents;
QTimer m_partialTorrentTimer;
QVector<QDir> m_watchedFolders;
QTimer m_watchTimer;
};

View File

@@ -35,16 +35,44 @@
#define QBT_APP_64BIT
#endif
inline const char C_TORRENT_FILE_EXTENSION[] = ".torrent";
inline const int MAX_TORRENT_SIZE = 100 * 1024 * 1024; // 100 MiB
const char C_TORRENT_FILE_EXTENSION[] = ".torrent";
const int MAX_TORRENT_SIZE = 100 * 1024 * 1024; // 100 MiB
template <typename T>
constexpr typename std::add_const_t<T> &asConst(T &t) noexcept { return t; }
constexpr typename std::add_const<T>::type &asConst(T &t) noexcept { return t; }
// Forward rvalue as const
template <typename T>
constexpr typename std::add_const_t<T> asConst(T &&t) noexcept { return std::move(t); }
constexpr typename std::add_const<T>::type asConst(T &&t) noexcept { return std::move(t); }
// Prevent const rvalue arguments
template <typename T>
void asConst(const T &&) = delete;
namespace List
{
// Replacement for the deprecated`QSet<T> QSet::fromList(const QList<T> &list)`
template <typename T>
QSet<T> toSet(const QList<T> &list)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
return {list.cbegin(), list.cend()};
#else
return QSet<T>::fromList(list);
#endif
}
}
namespace Vector
{
// Replacement for the deprecated `QVector<T> QVector::fromStdVector(const std::vector<T> &vector)`
template <typename T>
QVector<T> fromStdVector(const std::vector<T> &vector)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
return {vector.cbegin(), vector.cend()};
#else
return QVector<T>::fromStdVector(vector);
#endif
}
}

View File

@@ -161,7 +161,7 @@ bool Connection::acceptsGzipEncoding(QString codings)
return false;
};
const QVector<QStringRef> list = codings.remove(' ').remove('\t').splitRef(',', Qt::SkipEmptyParts);
const QVector<QStringRef> list = codings.remove(' ').remove('\t').splitRef(',', QString::SkipEmptyParts);
if (list.isEmpty())
return false;

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