Compare commits

...

208 Commits

Author SHA1 Message Date
sledgehammer999
7825d8627f Bump to 4.1.9 2019-10-27 17:26:03 +02:00
sledgehammer999
b798c591e7 Update Changelog 2019-10-27 17:13:26 +02:00
Chocobo1
9e7d21d7aa Update m4 scripts 2019-10-27 17:11:19 +02:00
Chocobo1
38a15b27f0 Regenerate build-aux scripts
It's been a while since the last update (26bf838a0b)
and there is a lot of changes in upstream.
2019-10-27 17:05:37 +02:00
sledgehammer999
cb31684a66 Merge pull request #11309 from sledgehammer999/webui_version
Bump Web API version
2019-10-20 23:07:49 +03:00
Mike Tzou
850cde19f7 Merge pull request #11328 from Chocobo1/backport
Use screen real physical DPI on Windows (backport)
2019-10-05 10:02:07 +08:00
Chocobo1
fc5b3b4f70 Use screen real physical DPI on Windows
Previously was using a hardcoded value which might lead to issues like #11234.
2019-10-04 12:05:13 +08:00
Mike Tzou
73fd4d83af Merge pull request #11319 from Chocobo1/backport
Preserve relative order when moving to top/bottom in queue (backport)
2019-10-04 12:01:33 +08:00
Chocobo1
811b525b1d Preserve relative order when moving to top/bottom in queue 2019-10-03 11:23:28 +08:00
sledgehammer999
0f8def9a49 Bump Web API version
Closes #11304
2019-10-01 17:31:55 +03:00
sledgehammer999
359b464958 Bump to 4.1.8 2019-09-23 21:56:30 +03:00
sledgehammer999
b5b0d68dd9 Update Changelog 2019-09-23 21:52:56 +03:00
Chocobo1
f48c49c248 Fix seeding failed after creating a new torrent
Closes #11252.
2019-09-23 21:47:37 +03:00
Chocobo1
11ca744548 Treat .magnet file extension as case insensitive
Closes #11200.
2019-09-23 21:47:35 +03:00
Chocobo1
82e6fc700e Fix filename validation on non-Windows OS
Closes #11191.
2019-09-23 21:47:34 +03:00
Mike Tzou
59fd70c638 Merge pull request #11240 from Chocobo1/backport
Revise HTML escaping in GUI (Backport)
2019-09-17 10:38:51 +08:00
Chocobo1
617bf767df Escape HTML in comment field 2019-09-16 23:48:12 +08:00
Chocobo1
55180e3598 Remove redundant HTML escaping
The text widget is already set to plaintext and doing HTML escaping will
not give us more security but only makes it harder to read.
2019-09-16 23:48:06 +08:00
Mike Tzou
56b62e6573 Merge pull request #11183 from Chocobo1/backport
Always save info dict when saving fastresume (backport to v4_1_x)
2019-09-04 12:09:14 +08:00
Chocobo1
b37e7b0340 Always save info dict when saving fastresume
Otherwise torrents loaded from fastresume won't have it and needs
to redownload it from elsewhere and slowing down the startup process.
This is also required for the future where we will drop loading the
`info` dict from .torrent files.
2019-09-03 14:37:45 +08:00
Mike Tzou
21aebaf16f Merge pull request #11074 from Chocobo1/backport
Fix translation issues (backport to v4_1_x)
2019-08-15 11:11:52 +08:00
Chocobo1
5792465317 Fix translation issues
By using disambiguation field instead of comment field to differentiate
translations.
2019-08-14 13:09:56 +08:00
Vladimir Golovnev
e98f44af63 Merge pull request #11067 from glassez/rss-api
Allow to refresh RSS item(s) via WebAPI
2019-08-13 19:52:10 +03:00
Vladimir Golovnev (Glassez)
40cf0203fb Allow to refresh RSS item(s) via WebAPI 2019-08-13 17:39:19 +03:00
sledgehammer999
05a82afeb6 Bump to 4.1.7 2019-08-04 14:17:20 +03:00
sledgehammer999
a456f1b0f9 Update Changelog 2019-08-04 14:14:21 +03:00
sledgehammer999
4acc44a5b0 Sync translations from Transifex and run lupdate/tstool.py 2019-08-04 13:50:34 +03:00
Mike Tzou
9c2a1146df Merge pull request #10991 from Chocobo1/backport
Backport to v4_1_x
2019-07-31 14:29:14 +08:00
Chocobo1
807abeae87 Fix messed up symbols in log 2019-07-31 14:28:05 +08:00
Chocobo1
dd2a0d0484 Fix incomplete file extension not applied for new torrents 2019-07-29 11:20:36 +08:00
Vladimir Golovnev
3f3400f43b Merge pull request #10967 from glassez/fastresume41
Save updated resume data for completed torrents (backport to v4.1.x)
2019-07-24 17:17:50 +03:00
Vladimir Golovnev (Glassez)
334b57a89a Save updated resume data for completed torrents
If fastresume data was rejected we need to save updated
resume data after torrent finishes rechecking.
2019-07-23 20:15:41 +03:00
Vladimir Golovnev (Glassez)
00d6c83ee5 Fix requested torrent resume data handling
Session should increase an appropriate counter each time
the torrent resume data is requested to save.
2019-07-23 20:15:23 +03:00
Mike Tzou
e8850c7a70 Merge pull request #10953 from Chocobo1/backport
Backport to v4_1_x
2019-07-21 21:19:05 +08:00
Chocobo1
2ef96eb218 Prevent command injection via "Run external program" function 2019-07-21 12:07:32 +08:00
Vladimir Golovnev
4682e31ab7 Merge pull request #10929 from glassez/backport
Fix torrent checking issues (backport to v4.1.x)
2019-07-17 17:50:35 +03:00
Vladimir Golovnev (Glassez)
988f7e2ef8 Don't break torrent checking 2019-07-15 20:02:08 +03:00
Vladimir Golovnev (Glassez)
6007913291 Ignore some actions on uninitialized torrents
Some actions can lead to an inconsistent state if applied
to an uninitialized torrent, so we just ignore them.
2019-07-15 20:02:04 +03:00
Vladimir Golovnev (Glassez)
cdcc7a210b Avoid race conditions when adding torrent 2019-07-15 20:01:59 +03:00
Vladimir Golovnev (Glassez)
a466ff5057 Fix torrent checking issues
Start all torrents auto-managed to prevent simultaneous checking
of multiple torrents.
Handle checking state of paused torrent to prevent it from being
resumed when qBittorrent is closed until checking isn't complete.
2019-07-15 20:01:43 +03:00
Mike Tzou
e954835579 Merge pull request #10869 from Chocobo1/backport
Backport to v4_1_x
2019-07-06 12:17:33 +08:00
Chocobo1
3e9be3a0e8 Use proper log message when there are no error 2019-07-06 12:16:43 +08:00
Chocobo1
4ab32a76f6 Fix torrent properties not saved for paused torrents 2019-07-06 12:16:23 +08:00
Chocobo1
bad60058df Restrict QLocalServer access
The default is world access which means even even unprivileged local
accounts can connect to it too.
2019-06-29 15:30:09 +08:00
Chocobo1
31a6ad1eb6 Drop suspiciously large data
This is to avoid exhausting system memory.
2019-06-29 15:30:04 +08:00
Mike Tzou
a8bfec081e Merge pull request #10848 from Chocobo1/backport
Backport to v4_1_x
2019-06-25 09:01:25 +08:00
Chocobo1
ae21d0f1e2 Remove limits of "Disk cache expiry interval" setting 2019-06-24 11:14:21 +08:00
Chocobo1
c599976b6f Remove upper limit of "Disk cache" setting 2019-06-24 11:13:50 +08:00
Mike Tzou
bcee784097 Merge pull request #10842 from Piccirello/v4_1_x
Backport to v4_1_x
2019-06-24 11:00:12 +08:00
Thomas Piccirello
697fc626cd Fix WebUI encoding of special characters 2019-06-22 15:52:19 -07:00
Mike Tzou
2f15ea9b54 Merge pull request #10829 from Chocobo1/backport
Backport to v4_1_x
2019-06-21 11:02:54 +08:00
Chocobo1
d03209a73d Fix crash when removing phantom tags
Normally a tag is stored in both session and torrent's fastresume.
A phantom tag is a tag that is stored in fastresume but not in
session.
This crash can occur when user resets his config file and choose
to remove tag from torrent.

Closes #10569.
2019-06-20 11:38:15 +08:00
Chocobo1
ac9ba255d8 Set wheel event to accepted only if we handle it 2019-06-20 11:38:02 +08:00
Chocobo1
9a7e79bd0e Improve handleFileErrorAlert error message 2019-06-20 11:37:40 +08:00
Mike Tzou
e8be3bf939 Merge pull request #10772 from Chocobo1/backport
Backport to v4_1_x
2019-06-17 01:00:35 +08:00
Chocobo1
74e52746b1 Reorder if conditions slightly 2019-06-16 12:37:16 +08:00
Chocobo1
8d26a221e0 Fix updated save path not saved for paused torrents 2019-06-16 12:37:16 +08:00
Chocobo1
3fdab88eb7 Log save_resume_data_failed_alert 2019-06-16 12:37:16 +08:00
Chocobo1
d376d912b3 Don't remove parent directories
QDir::rmpath removes *all* parent directories while QDir::rmdir removes
the specified directory.
2019-06-16 12:30:57 +08:00
Chocobo1
e329c41ef2 Properly remove empty leftover folders after rename
TorrentInfo::origFilePath will return the very original path from
.torrent file, not the most recent file path before the rename operation
and thus the code would not be working as we expected.
2019-06-16 12:30:42 +08:00
silverqx
01e4179555 Focus behavior row in Options dialog 2019-06-15 11:50:28 +08:00
Chocobo1
06f503b5df Fix unable to rename folder on Windows
The bug occurs when the new path and old path only differ by letter case.
2019-06-10 10:34:15 +08:00
Chocobo1
e2f3dad7b8 Move renameSelectedFile(BitTorrent::TorrentInfo &) 2019-06-10 10:34:10 +08:00
Chocobo1
377f31085c Move renameSelectedFile(BitTorrent::TorrentHandle *) 2019-06-10 10:34:03 +08:00
Chocobo1
ec13d195f8 Fix unable to control add torrent dialogs when opened simultaneously 2019-06-06 23:31:27 +08:00
Chocobo1
c01aed8d90 Remove redundant disconnect()
The dialog is going out-of-scope in these instance and the signal-slot connection will disconnect
automatically.
2019-06-06 23:29:53 +08:00
Vladimir Golovnev
ad7b8a9bfa Merge pull request #10757 from glassez/backport
Backport to v4.1.x
2019-06-04 17:59:10 +03:00
Vladimir Golovnev (Glassez)
5bba1ed208 Ignore RSS articles with non-unique identifiers 2019-06-04 15:08:35 +03:00
Vladimir Golovnev (Glassez)
fe94e14bcc Perform more RSS parsing in working thread 2019-06-04 15:06:29 +03:00
Vladimir Golovnev (Glassez)
b0af479ab9 Disable "Upload mode" when start preloaded torrent 2019-06-04 15:01:10 +03:00
Mike Tzou
24ff369f29 Merge pull request #10731 from Chocobo1/backport
Backport to v4_1_x
2019-06-01 10:07:23 +08:00
Vladimir Golovnev
979c9a7094 Merge pull request #10728 from glassez/fix-seqdl
Fix sequential downloading when redirected
2019-05-31 08:13:50 +03:00
Chocobo1
7b90ac52c1 Fix wrong comparison result
The QString::toInt() might overflow when the string is long.

Closes #10706.
2019-05-31 12:20:39 +08:00
Vladimir Golovnev (Glassez)
ecfbda78bc Fix sequential downloading when redirected 2019-05-30 16:06:18 +03:00
Mike Tzou
9ba7470815 Merge pull request #10615 from Chocobo1/backport
Backport to v4_1_x
2019-05-23 22:14:18 +08:00
Chocobo1
6394467cc7 Fix typos 2019-05-20 17:45:48 +08:00
Chocobo1
f6d72fa79f Fix assertion fail
When fileSize == 0, the second index could be smaller than the first index,
thus trigger the assert check in IndexInterval constructor.

Closes #10611.
2019-05-16 10:49:00 +08:00
dzmat
32ed5f1c8e Change number of time axis divisions from 5 to 6 for convenience 2019-05-15 20:36:00 +08:00
dzmat
5026da5773 Add 12 hour and 24 hour speed graphs 2019-05-15 20:35:50 +08:00
Evgeny Lensky
ef130e4438 Change "Add new torrent" dialog to horizontal layout 2019-05-15 10:19:37 +08:00
jerrymakesjelly
4fbd52c2d5 Change the speed unit from Bytes/s to KiB/s
Updated the adding torrent dialogs in WebUI. Closes #10017.
2019-05-12 13:51:35 +08:00
Matan Bareket
8f29b70c1e Download RSS enclosure element if no proper MIME type is found
In the case where an RSS feed doesn't have the "enclosure" element
with type "application/x-bittorrent", fallback to the last enclosure
element which has no "type" attribute.
2019-05-11 23:39:57 +08:00
Ekin Dursun
9a4dd3ea9d Don't turn screen blank when closed to system tray
Closes #9240 and possibly #9121, as @zeule suggested here: https://github.com/qbittorrent/qBittorrent/issues/9240#issuecomment-408025722.
2019-05-11 12:25:42 +08:00
Chocobo1
fcd3bb6918 Refactor HTTP query parsing 2019-05-10 09:20:03 +08:00
Chocobo1
9f69fd8750 Fix '+' char not decoded to space correctly
Closes #10606.
2019-05-10 09:19:56 +08:00
sledgehammer999
ea7e47d113 Bump to 4.1.6 2019-05-05 20:44:39 +03:00
sledgehammer999
294bb26996 Update Changelog 2019-05-05 20:40:50 +03:00
sledgehammer999
4b2e9dba51 Sync translations from Transifex and run lupdate/tstool.py 2019-05-05 20:20:06 +03:00
sledgehammer999
1d9dcde99b Merge pull request #10567 from Chocobo1/backport
Backport #10445 to v4_1_x
2019-05-05 19:52:14 +03:00
Chocobo1
32bf448725 Initialize class variable via constructor 2019-05-03 23:03:33 +08:00
Chocobo1
732d5d6db9 Disable downloading tracker favicons by default
Workaround for a crash in Qt networking library, source:
https://github.com/qbittorrent/qBittorrent/issues/9667#issuecomment-464445025

Closes #9667.
2019-05-03 22:56:16 +08:00
Mike Tzou
3b325106da Merge pull request #10565 from Chocobo1/backport
Backport to v4_1_x
2019-05-03 19:58:31 +08:00
Chocobo1
3aeca37c5d Fix "Create subfolder" option is not working in WebUI
Closes #10392.
2019-05-03 12:40:45 +08:00
Mike Tzou
4253515736 Merge pull request #10496 from Chocobo1/backport
Backport to v4_1_x
2019-04-17 13:13:25 +08:00
Chocobo1
e365d57063 Fix unsafe type narrowing
Appending the warning below:
qBittorrent\src\base/utils/version.h(176): warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data
qBittorrent\src\base/utils/version.h(185): note: see reference to function template instantiation 'std::array<T,2> Utils::Version<T,2,2>::parseList<StringsList>(const StringsList &)' being compiled
        with
        [
            T=unsigned short,
            StringsList=QList<QByteArray>
        ]
2019-04-16 12:37:46 +08:00
Chocobo1
df6df20969 Use QSet for tracking server connections
We don't need to maintain order between connections so QSet would be more suitable.
2019-04-16 12:34:09 +08:00
Chocobo1
4e5a85dda5 Remove closed connections immediately
Previously it relied on a timer to drop dead connections but that proved to
be too slow when there is an incoming burst of connections.

Fixes #10487.
2019-04-16 12:34:00 +08:00
Mike Tzou
bad603454b Merge pull request #10474 from thalieht/backport
Backport #10464 to v4_1_x
2019-04-11 10:16:08 +08:00
thalieht
7b006a47ba Show user friendly size in error 2019-04-10 12:08:47 +03:00
thalieht
11da8b82e8 Increase the download size limit to 100 MiB 2019-04-10 12:02:43 +03:00
Mike Tzou
383a5f11bc Merge pull request #10468 from Chocobo1/backport
Backport recent PRs to v4_1_x
2019-04-10 16:50:19 +08:00
Chocobo1
76ab5f12c5 Work around the crash occurred in QTimer
See Qt commit:
https://code.qt.io/cgit/qt/qtbase.git/commit/src/corelib/kernel/qtimer.cpp?id=a623fe8d2a60ff333d5779f877e3b20f0e141ff1

Fixes #9985.
2019-04-09 13:57:53 +08:00
Chocobo1
be74987084 Correctly handle '+' sign in x-www-form-urlencoded data
Fixes #10451.
2019-04-09 13:57:35 +08:00
Chocobo1
8f6c305d14 Update .appdata descriptions 2019-03-25 13:27:29 +02:00
Chocobo1
e29b9655eb Use reverse DNS convention for metadata files naming
Also update appdata install path.

Closes #10111.
2019-03-25 13:27:22 +02:00
sledgehammer999
ae7fa9ea82 Merge pull request #10403 from thalieht/backport-search-enter-key
Backport #10400 to v4_1_x branch
2019-03-25 13:22:03 +02:00
thalieht
fee9030337 Make num enter key work the same as return in searchjobwidget
Backport #10400 to v4_1_x branch.
2019-03-23 10:51:07 +02:00
Peter Eszlari
f48d057c47 Linux/Wayland: make window title bar icon work 2019-03-17 21:55:50 +02:00
sledgehammer999
f14573307c Don't query Google for tracker favicons
It is a bad idea for user privacy to automatically contact a 3rd party
about tracker domains. Especially when the user isn't informed about
this and the 3rd party is Google.

Reverts 1b9882b3a3
2019-03-17 21:55:50 +02:00
sledgehammer999
560ba8c0b8 Update copyright year 2019-03-17 21:55:49 +02:00
Nick Korotysh
4b2376c4fd Cleanup Info.plist
Added few required/recommended and removed obsolete/deprecated keys
according to Apple developers documentation.
Changed CFBundleIdentifier according to reverse DNS convention.
Updated copyright year.
2019-03-17 21:55:45 +02:00
Nick Korotysh
76faed3818 Draw progress bar in Fusion style on macOS systems
Qt has a bug QTBUG-72558, which leads to incorrect progress bar
position, when drawing it in delegate.
Also, since OS X 10.10 Yosemite macOS default style was changed,
and progress bars became very tiny and without text (percentage).
These two cases make qBittorrent look pretty awful, but drawing
progress bar in Fusion style solves both issues.
2019-03-17 21:55:07 +02:00
sledgehammer999
62657d9fda Mention more translators
Closes #10043
2019-03-17 21:55:07 +02:00
Mike Tzou
5877308a49 Merge pull request #10272 from Chocobo1/login_v41x
Prevent login credential appearing in URL (for v4_1_x branch)
2019-02-10 13:31:20 +08:00
Chocobo1
24dcbe7d43 Fix wrong arg placeholder 2019-02-09 21:26:08 +08:00
Chocobo1
7649fe0a0e Display warning when Javascript is disabled 2019-02-09 15:32:43 +08:00
Chocobo1
b3b334da77 Prevent login credential appearing in URL
Closes #10221.
2019-02-09 15:28:31 +08:00
Vladimir Golovnev
03a55da260 Merge pull request #10266 from glassez/ordered-recheck
Force recheck multiple torrents one by one
2019-02-09 07:14:13 +03:00
Vladimir Golovnev (Glassez)
1a9eadf8e6 Force recheck multiple torrents one by one
Closes #9120.
2019-02-08 08:25:16 +03:00
Vladimir Golovnev
7b3fb2a35a Merge pull request #10229 from glassez/http-request-41
Separate URL components before percent-decoding
2019-01-28 16:54:02 +03:00
Vladimir Golovnev (Glassez)
a55ea29919 Separate URL components before percent-decoding
Allow special characters in query string parameters.
Closes #9116.
2019-01-28 08:54:36 +03:00
sledgehammer999
264b689912 Fix TravisCI build on macOS again 2018-12-27 22:29:38 +02:00
sledgehammer999
684cf82f89 Fix TravisCI build on macOS 2018-12-27 22:11:11 +02:00
sledgehammer999
3f0e0a319a Bump to 4.1.5 2018-12-24 19:24:16 +02:00
sledgehammer999
0b4d9c72a7 Update Changelog 2018-12-24 19:20:07 +02:00
sledgehammer999
ff71f6bcd9 Sync translations from Transifex and run lupdate 2018-12-24 18:52:35 +02:00
sledgehammer999
7a5c5baad1 Update transifex config file 2018-12-24 18:52:34 +02:00
thalieht
a18976d0b5 Fix regression on resuming torrents without metadata 2018-12-24 18:19:30 +02:00
Chocobo1
6d836ea49c Change qbt exit message to HTML5 2018-12-24 18:19:29 +02:00
Chocobo1
2e97311147 Unify translation files loading action
Since it is possible alternative WebUI could be coded in languages other than English,
WebUI must be able to load user-provided webui_en.qm.
At least one translated string must exist in order to generate an usable .qm file.
2018-12-24 18:19:28 +02:00
sledgehammer999
57bc564b2c Use configured locale only for translating
Don't use other aspects of it eg for date formatting. We should depend
on the system locale for all these. The user probably likes it that way,
otherwise he would have changed it.
2018-12-24 18:19:27 +02:00
Stephen Dawkins
1295f1e31f Keep track of REPACK/PROPER downloads
When using the smart episode filter, if the episode contains REPACK and/or
PROPER, these should be stored to prevent it from redownloading a duplicate
episodes.

Closes #9898.
2018-12-24 18:19:26 +02:00
sledgehammer999
4916ed0efb Update transifex config file 2018-12-24 18:19:25 +02:00
sledgehammer999
f15f99cb27 Sync translations from Transifex and run lupdate 2018-12-24 18:19:22 +02:00
sledgehammer999
93365d3b20 Update WebUI .ts files 2018-12-24 18:19:11 +02:00
Chocobo1
c756ab021d Upgrade TravisCI to xenial
* Remove cmake installation, it is already pre-installed by TravisCI.
* Limit ccache cache size to 512 MB. Previously the size was 5 GB for
macOS and it took 1~3 mins just for packing & uploading the cache,
limiting the size should shorten total build time.
2018-12-17 00:49:09 +02:00
Nick Korotysh
34528dd544 Make file icon look like other macOS icons 2018-12-17 00:49:08 +02:00
Chocobo1
9380209afb Revise CSP header
The majority of the CSP is tuned for built-in WebUI, it may not be
suitable for alternative UI.

Also add QLatin1String to strings. This code path is called repeatedly,
it is worth adding QLatin1String to squeeze out the last bit of
performance.
2018-12-17 00:49:07 +02:00
Chocobo1
be2895ac6f Enforce referrer-policy in WebUI
This stops leaking private data to other websites via Referrer header.
2018-12-17 00:49:06 +02:00
Thomas Piccirello
e26d4642b8 Add torrent name filtering to WebUI
Closes #721
2018-12-17 00:49:05 +02:00
Thomas Piccirello
f470972bd4 Send numeric status without translation 2018-12-17 00:49:04 +02:00
Thomas Piccirello
443378c041 Remove condition for unsupported libtorrent version 2018-12-17 00:49:02 +02:00
Thomas Piccirello
e20dbe34a4 Simplify map initialization 2018-12-17 00:49:01 +02:00
Thomas Piccirello
86bde47a06 Add WebUI Trackers context menu 2018-12-17 00:49:00 +02:00
Thomas Piccirello
e273c777c7 Add DHT, PeX, and LSD to WebUI Tracker list 2018-12-17 00:48:59 +02:00
Thomas Piccirello
17845c6b25 Add additional Tracker columns to WebUI 2018-12-17 00:48:58 +02:00
Thomas Piccirello
27827ce16a Use const where appropriate 2018-12-17 00:48:57 +02:00
Thomas Piccirello
b444ecc6af Reorder and rename Tracker list context menu option
Adds an ellipses to indicate that the Edit option opens a dialog. Also moves Edit to top of the list to convey action's prominence.
2018-12-17 00:48:56 +02:00
Thomas Piccirello
34995350ee Rename Tracker List columns
"Received" renamed to "Peers", "Peers" renamed to "Leeches".
2018-12-17 00:48:55 +02:00
sledgehammer999
73ceee52f8 Bump Web API version 2018-12-17 00:48:54 +02:00
Thomas Piccirello
85a3ba0eed Update Copyright email address 2018-12-17 00:48:53 +02:00
Thomas Piccirello
86cce76e9d Fix display bugs in WebUI Files tab. Remove <IE9 support
Priority select boxes would frequently go blank due to an unexpected priority value. On first load, the torrent-scoped file checkbox's state was inconsistent with the state of the torrent's files.
2018-12-17 00:48:52 +02:00
Thomas Piccirello
3358fd8e91 Fix incorrect priority value sent from WebUI
Closes #9070.
2018-12-17 00:48:51 +02:00
Thomas Piccirello
120965f823 Set priority for multiple files in one WebAPI request
Closes #6259.
2018-12-17 00:48:50 +02:00
Thomas Piccirello
e70ee9a5b6 Replace prio namespace with FilePriority enum class 2018-12-17 00:48:48 +02:00
Thomas Piccirello
a2d8e84e83 Match WebUI Peers table column order to GUI 2018-12-17 00:48:47 +02:00
Chocobo1
4a3648a693 Use gcc-5 for TravisCI linux builds
Remove workarounds for CXXFLAGS.
Using 3 compilation jobs should cause process trashing, tune it down to 2.
TravisCI container builds is deprecated, so remove the `sudo: false` command.
2018-12-17 00:48:43 +02:00
Chocobo1
baad45e638 Use CC, CXX from environment when available 2018-12-17 00:47:01 +02:00
Chocobo1
d9cb00aab2 Use correct locale to display date 2018-12-17 00:46:54 +02:00
Vladimir Golovnev (Glassez)
d703d98836 Show error message when Session failed to start 2018-12-17 00:41:30 +02:00
Thomas Piccirello
2f0646e7f0 Fetch data less frequently when torrents tab isn't visible 2018-12-17 00:41:28 +02:00
Thomas Piccirello
1a8a6dcef7 Add Search tab to WebUI
Closes #859, #8107.
2018-12-17 00:41:27 +02:00
Thomas Piccirello
990f961126 Allow tables to be added without a parent panel 2018-12-17 00:41:26 +02:00
Thomas Piccirello
06f04dea19 Simplify implementation 2018-12-17 00:41:25 +02:00
Thomas Piccirello
8eced2ef1f Add ability to pass urls to the webui download page 2018-12-17 00:41:24 +02:00
Tom Piccirello
1e486ea92e Fix JavaScript error
Fixes a JavaScript error caused by the element lookup returning null
2018-12-17 00:41:22 +02:00
Thomas Piccirello
b47f38675e Disallow setting a blank alternative WebUI location 2018-12-17 00:41:21 +02:00
Thomas Piccirello
864f3393a0 Add slow torrent options 2018-12-17 00:41:20 +02:00
Thomas Piccirello
cebef74326 Add "Use alternative Web UI" option 2018-12-17 00:41:19 +02:00
Thomas Piccirello
e257b35cac Add "Apply rate limit to peers on LAN" option 2018-12-17 00:41:17 +02:00
Thomas Piccirello
1f33991e4b Add email "From" option 2018-12-17 00:41:16 +02:00
Thomas Piccirello
794053f212 Set WebUI download options using set preferences
"Start torrent" and "Create subfolder" are now set depending on the user's set preferences, which matches the behavior exhibited by the GUI.
2018-12-17 00:41:14 +02:00
Thomas Piccirello
3a130e1f74 Show list of categories on WebUI download page 2018-12-17 00:41:13 +02:00
Thomas Piccirello
3423f93230 Hide WebUI text input for custom monitor save locations 2018-12-17 00:41:12 +02:00
Thomas Piccirello
2219167253 Add "When adding a torrent" options 2018-12-17 00:41:10 +02:00
Thomas Piccirello
a0a32b89a6 Add WebUI Auto TMM options 2018-12-17 00:41:08 +02:00
Thomas Piccirello
59162bf426 Replace all line breaks and fix legend code style. 2018-12-17 00:41:07 +02:00
Thomas Piccirello
dfd148f55f Add speed limit icons to WebUI Speed options 2018-12-17 00:41:05 +02:00
Thomas Piccirello
3af720b3bc Add WebUI Random port button and proxy unencrypted password notice 2018-12-17 00:41:04 +02:00
Thomas Piccirello
11240d0837 Replace WebUI Options fixed-width labels
This allows the labels to auto-expand based on the language used, and also removes unnecessary whitespace. Additionally, this results in a look more consistent with the GUI which right-aligns labels.
2018-12-17 00:40:53 +02:00
Thomas Piccirello
e64fd9c544 Reorder WebUI options to match GUI 2018-12-17 00:28:35 +02:00
FranciscoPombal
50ef812427 Add checking_mem_usage option to AdvancedSettings 2018-12-17 00:28:34 +02:00
thalieht
bd4d2fa424 Combine qAsConst() with copyAsConst() to asConst() 2018-12-17 00:28:33 +02:00
thalieht
e2ee928017 Convert all foreach() to range-based for() 2018-12-17 00:28:32 +02:00
thalieht
62e71a15a4 Fix coding style for various things 2018-12-17 00:28:31 +02:00
thalieht
c62127e9f1 Save option to start minimized in Mac 2018-12-17 00:28:29 +02:00
Chocobo1
2171d579ee Fix typo 2018-12-17 00:28:28 +02:00
Chocobo1
6e5a969e2d Use ip parameter from tracker request if provided
Closes #9949.
2018-12-17 00:28:28 +02:00
Chocobo1
bfbc7ef28a Use QHostAddress for storing IP 2018-12-17 00:28:26 +02:00
sledgehammer999
b1cefbf9b5 Autotools: Replace CPPFLAGS with CXXFLAGS 2018-12-17 00:28:25 +02:00
sledgehammer999
201638854e Autotools: Print Boost LDFLAGS nicer 2018-12-17 00:28:24 +02:00
sledgehammer999
847ecdeedb Autotools: Improve handling of C++ mode 2018-12-17 00:28:23 +02:00
Chocobo1
acc159fa60 Fix wrong locale used in log message 2018-12-17 00:28:22 +02:00
Chocobo1
bb7e80a8a6 Fix weekday names translations
Closes #9933.
2018-12-17 00:28:21 +02:00
Chocobo1
39973f1bb1 Fix strings not translated
Closes #9934.
2018-12-17 00:28:21 +02:00
Chocobo1
1e9151364a Clean up code 2018-12-17 00:28:20 +02:00
Vladimir Golovnev (Glassez)
fd50d6e9af Save torrents queue in separate file 2018-12-17 00:28:19 +02:00
Vladimir Golovnev (Glassez)
427acf0c46 Fix signed/unsigned integers comparison warning 2018-12-17 00:28:18 +02:00
Thomas Piccirello
f0a50424be Allow WebUI sidebar to be collapsed 2018-12-17 00:28:18 +02:00
Thomas Piccirello
aded9afc0e Show ellipsis when WebUI sidebar is too narrow 2018-12-17 00:28:17 +02:00
Thomas Piccirello
060b7480db Only instantiate SearchPluginManager as needed 2018-12-17 00:28:16 +02:00
Tom Piccirello
7f2a01dcd6 Fix WebUI bug on override of Start Download option
Disabled form values aren't submitted, causing the add_paused value not to be sent when Start Torrent was checked. qBittorrent would then fall back to the global Start Download preference.

Closes #9855.
2018-12-17 00:28:15 +02:00
Chocobo1
fef0e70c9f Fix missing words in WebUI
This is because Qt translator returns empty string when the translation
is not provided, now we fallback to the original string from source code.

Closes #9868.
2018-12-17 00:28:14 +02:00
Thomas Piccirello
9cc112aa4e Add SameSite attribute to WebUI session cookie
This attribute prevents the cookie from being submitted on any cross-site request, strongly limiting CSRF.

Closes #9877.
2018-12-17 00:28:14 +02:00
Chocobo1
44d4d41365 Put WebUI security related options into a groupbox 2018-12-17 00:28:13 +02:00
Chocobo1
a21c386dbf Add option for WebUI Host header validation
Closes #9743.
2018-12-17 00:28:12 +02:00
Thomas Piccirello
1c4139906a Show icon in WebUI sorted column 2018-12-17 00:28:11 +02:00
Chocobo1
1a21f45c75 Implement proper C++11 mode detection
Newer compilers have C++14 mode as default and package maintainers tend
to not specifying a C++ version when building a package, this causes
compatibility issues when (for example) qbt is compiled in C++11 and
dependency lib is in C++14. See issue #9485.

What this commit does:
1. Checks if compiler supports at least C++11
2. Checks if compiler is set in at least C++11 mode
2018-12-17 00:28:04 +02:00
288 changed files with 93169 additions and 70665 deletions

View File

@@ -4,6 +4,8 @@ os:
- linux
- osx
dist: xenial
env:
matrix:
# Uncomment when Travis upgraded "Ubuntu 12.04 LTS" to a newer version whose repo will have a more up-to-date libtorrent package
@@ -37,11 +39,6 @@ cache:
directories:
- $HOME/hombebrew_cache
# opt-in Ubuntu Trusty
dist: trusty
# container-based builds
sudo: false
addons:
coverity_scan:
project:
@@ -53,34 +50,30 @@ addons:
notification_email: sledgehammer999@qbittorrent.org
apt:
sources:
# sources list: https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
- ubuntu-toolchain-r-test
#- boost-latest
# sources list: https://github.com/travis-ci/apt-source-safelist/blob/master/ubuntu.json
- sourceline: 'ppa:qbittorrent-team/qbittorrent-stable'
- sourceline: 'ppa:beineri/opt-qt551-trusty'
- sourceline: 'ppa:adrozdoff/cmake'
packages:
# packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
# packages list: https://github.com/travis-ci/apt-package-safelist/blob/master/ubuntu-trusty
- [autoconf, automake, colormake]
- [cmake, ninja-build]
- [ninja-build]
- libssl-dev
- [libboost-dev, libboost-system-dev]
- libtorrent-rasterbar-dev
- [qt55base, qt55svg, qt55tools]
- [gcc-6, g++-6]
- [qtbase5-dev, qttools5-dev-tools, libqt5svg5-dev]
before_install:
# only allow specific build for coverity scan, others will stop
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ] && ! [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" = "RC_1_0" -a "$gui" = true -a "$build_system" = "qmake" ]; then exit ; fi
- shopt -s expand_aliases
- alias make="colormake -j3" # Using nprocs/2 sometimes may fail (gcc is killed by system)
- alias make="colormake -j2" # Using nprocs/2 sometimes may fail (gcc is killed by system)
- qbt_path="$HOME/qbt_install"
- |
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH=/opt/qt55/lib/pkgconfig:$PKG_CONFIG_PATH"
else
qbtconf="$qbtconf --prefix="$qbt_path""
qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH"
CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedefs -Wno-inconsistent-missing-override"
fi
# options for specific branches
@@ -89,14 +82,6 @@ before_install:
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
# setup virtual display for after_success target
if [ "$gui" = true ]; then export "DISPLAY=:99.0" && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 ; fi ;
# Qt 5
PATH=/opt/qt55/bin:${PATH}
if [ "$build_system" = "cmake" ]; then
COMPILER_VERSION=6
export CXX="${CXX}-${COMPILER_VERSION}" CC="${CC}-${COMPILER_VERSION}"
fi
fi
# print settings
@@ -120,7 +105,7 @@ install:
# dependencies
brew update > /dev/null
brew outdated "pkg-config" || brew upgrade "pkg-config"
brew install colormake ccache zlib qt libtorrent-rasterbar
brew install colormake ccache zlib qt openssl libtorrent-rasterbar
PATH="/usr/local/opt/ccache/libexec:$PATH"
brew link --force zlib qt
@@ -130,13 +115,14 @@ install:
sudo ln -s /usr/local/opt/qt/mkspecs /usr/local/mkspecs
sudo ln -s /usr/local/opt/qt/plugins /usr/local/plugins
MY_CMAKE_OPENSSL_HINT="-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl/"
fi
MY_CMAKE_OPENSSL_HINT="-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl/"
fi
- |
if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
export use_ccache=true
ccache -M 512M
ccache -V && ccache --show-stats && ccache --zero-stats
fi
@@ -155,16 +141,7 @@ script:
BUILD_TOOL="ninja"
fi
if [ "$build_system" = "qmake" ]; then
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
# For some reason for RC_1_1 we need to also specify the OpenSSL compiler/linker flags
# Homebrew doesn't symlink OpenSSL for security reasons
./bootstrap.sh && ./configure $qbtconf CXXFLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --cflags openssl)" LDFLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --libs openssl)"
sed -i "" -e "s/^\(CC.*&&\).*$/\1 $CC/" src/Makefile # workaround for Qt & ccache: https://bugreports.qt.io/browse/QTBUG-31034
sed -i "" -e "s/^\(CXX.*&&\).*$/\1 $CXX/" src/Makefile
sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile
else
./bootstrap.sh && ./configure $qbtconf
fi
./bootstrap.sh && ./configure $qbtconf CXXFLAGS="$CXXFLAGS"
BUILD_TOOL="make"
fi
- $BUILD_TOOL && $BUILD_TOOL install

View File

@@ -10,10 +10,18 @@ type = QT
minimum_perc = 23
mode = developer
[qbittorrent.qbittorrentdesktop_master]
source_file = dist/unix/qbittorrent.desktop
source_file = dist/unix/org.qbittorrent.qBittorrent.desktop
source_lang = en
type = DESKTOP
minimum_perc = 23
mode = developer
[qbittorrent.qbittorrent_webui]
file_filter = src/webui/www/translations/webui_<lang>.ts
lang_map = pt: pt_PT
source_file = src/webui/www/translations/webui_en.ts
source_lang = en
type = QT
minimum_perc = 23
mode = developer

122
Changelog
View File

@@ -1,3 +1,125 @@
* Sun Oct 27 2019 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.9
- BUGFIX: Preserve relative order when moving to top/bottom in queue (Chocobo1)
- WINDOWS: Use real physical screen DPI (Chocobo1)
- WEBUI: Bump Web API version
* Mon Sep 23 2019 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.8
- BUGFIX: Fix filename validation on non-Windows OS (Chocobo1)
- BUGFIX: ScanFolders/FileSystemWatcher now detect magnet files with case insensitivity in filename (Chocobo1)
- BUGFIX: Fix failed seeding after creating a torrent and auto-adding it to the session (Chocobo1)
* Sun Aug 04 2019 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.7
- FEATURE: Add 12 hour and 24 hour speed graphs (dzmat)
- FEATURE: Change "Add new torrent" dialog to horizontal layout (Evgeny Lensky)
- BUGFIX: Fix messed up symbols in log (Chocobo1)
- BUGFIX: Fix incomplete file extension not applied for new torrents (Chocobo1)
- BUGFIX: Save updated resume data for completed torrents (Vladimir Golovnev (Glassez))
- BUGFIX: Fix requested torrent resume data handling (Vladimir Golovnev (Glassez))
- BUGFIX: Prevent command injection via "Run external program" function (Chocobo1)
- BUGFIX: Avoid race conditions when adding torrent (Vladimir Golovnev (Glassez))
- BUGFIX: Fix torrent checking issues (Vladimir Golovnev (Glassez))
- BUGFIX: Use proper log message when there are no error (Chocobo1)
- BUGFIX: Fix torrent properties not saved for paused torrents (Chocobo1)
- BUGFIX: Some improvements on qtsingleapplication code (Chocobo1)
- BUGFIX: Remove limits of "Disk cache expiry interval" setting (Chocobo1)
- BUGFIX: Remove upper limit of "Disk cache" setting (Chocobo1)
- BUGFIX: Fix crash when removing phantom tags (Chocobo1)
- BUGFIX: Improve handleFileErrorAlert error message (Chocobo1)
- BUGFIX: Fix updated save path not saved for paused torrents (Chocobo1)
- BUGFIX: Log save_resume_data_failed_alert (Chocobo1)
- BUGFIX: Don't remove parent directories (Chocobo1)
- BUGFIX: Properly remove empty leftover folders after rename (Chocobo1)
- BUGFIX: Focus behavior row in Options dialog (silverqx)
- BUGFIX: Fix unable to rename folder on Windows when same is used in different case(Chocobo1)
- BUGFIX: Fix unable to control add torrent dialogs when opened simultaneously (Chocobo1)
- BUGFIX: Disable "Upload mode" when start preloaded torrent (Vladimir Golovnev (Glassez))
- BUGFIX: Fix wrong comparison result when sorting items(Chocobo1)
- BUGFIX: Fix sequential downloading when redirected (Vladimir Golovnev (Glassez))
- BUGFIX: Fix typos (Chocobo1)
- BUGFIX: Fix assertion fail (Chocobo1)
- BUGFIX: Change number of time axis divisions from 5 to 6 for convenience (dzmat)
- BUGFIX: Don't turn window blank when closed to system tray (Ekin Dursun)
- WEBUI: Fix WebUI encoding of special characters (Thomas Piccirello)
- WEBUI: Change the speed unit from Bytes/s to KiB/s for the rate limiter(jerrymakesjelly)
- WEBUI: Fix '+' char not decoded to space correctly (Chocobo1)
- RSS: Ignore RSS articles with non-unique identifiers (Vladimir Golovnev (Glassez))
- RSS: Perform more RSS parsing in working thread (Vladimir Golovnev (Glassez))
- RSS: Download RSS enclosure element if no proper MIME type is found (Matan Bareket)
* Sun May 05 2019 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.6
- BUGFIX: Force recheck multiple torrents one by one in all possible cases. Closes #9120 (glassez)
- BUGFIX: Don't query Google for tracker favicons, for privacy reasons (sledgehammer999)
- BUGFIX: Work around the crash occurred in QTimer. Closes #9985 (Chocobo1)
- BUGFIX: Increase the .torrent file download size limit to 100 MiB (thalieht)
- BUGFIX: Disable downloading tracker favicons by default. Works around reported crashes in Linux. Closes #9667 (Chocobo1)
- WEBUI: Separate URL components before percent-decoding. Allow special characters in query string parameters. Closes #9116 (glassez)
- WEBUI: Prevent login credential appearing in URL. Closes #10221 (Chocobo1)
- WEBUI: Display warning when Javascript is disabled (Chocobo1)
- WEBUI: Fix translatable strings (Chocobo1)
- WEBUI: Correctly handle '+' sign in x-www-form-urlencoded data. Closes #10451 (Chocobo1)
- WEBUI: Remove closed connections immediately. Closes #10487 (Chocobo1)
- WEBUI: Fix "Create subfolder" option is not working. Closes ##10392 (Chocobo1)
- SEARCH: Make num enter key work the same as return in searchjobwidget (thalieht)
- LINUX: Make window title bar icon work in Wayland (Peter Eszlari)
- LINUX: Update appdata.xml file (Chocobo1)
- MACOS: Fix progress bar drawing by using different style than native (Nick Korotysh)
- MACOS: Updated and cleaned up fields in Info.plist (Nick Korotysh)
- OTHER: Mention more translators in the about tab. Closes #10043 (sledgehammer999)
* Mon Dec 24 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.5
- FEATURE: Add checking_mem_usage option to AdvancedSettings (FranciscoPombal)
- FEATURE: Save torrents queue in separate file. Now a new file named 'queue' is created, saving on each line the infohash of each queued torrent in sorted order. (glassez)
- BUGFIX: Fix regression on resuming torrents without metadata (thalieht)
- BUGFIX: Reorder and rename Tracker list context menu option (Thomas Piccirello)
- BUGFIX: Rename Tracker List columns (Thomas Piccirello)
- BUGFIX: Show error message when Session failed to start (glassez)
- BUGFIX: Embedded tracker: Use ip parameter from tracker request if provided (Chocobo1)
- BUGFIX: Fix weekday names translations (Chocobo1)
- BUGFIX: Fix strings not translated (Chocobo1)
- WEBUI: Change qBittorrent exit message to HTML5 (Chocobo1)
- WEBUI: Revise CSP header (Chocobo1)
- WEBUI: Enforce referrer-policy in WebUI (Chocobo1)
- WEBUI: Add torrent name filtering to WebUI (Thomas Piccirello)
- WEBUI: Send numeric status without translation (Thomas Piccirello)
- WEBUI: Add WebUI Trackers context menu (Thomas Piccirello)
- WEBUI: Add DHT, PeX, and LSD to WebUI Tracker list (Thomas Piccirello)
- WEBUI: Add additional Tracker columns to WebUI (Thomas Piccirello)
- WEBUI: Bump Web API version
- WEBUI: Fix display bugs in WebUI Files tab. Remove <IE9 support (Thomas Piccirello)
- WEBUI: Fix incorrect priority value sent from WebUI (Thomas Piccirello)
- WEBUI: Set priority for multiple files in one WebAPI request (Thomas Piccirello)
- WEBUI: Match WebUI Peers table column order to GUI (Thomas Piccirello)
- WEBUI: Fetch data less frequently when torrents tab isn't visible (Thomas Piccirello)
- WEBUI: Add Search tab to WebUI (Thomas Piccirello)
- WEBUI: Add ability to pass urls to the webui download page (Thomas Piccirello)
- WEBUI: Fix JavaScript error (Tom Piccirello)
- WEBUI: Disallow setting a blank alternative WebUI location (Thomas Piccirello)
- WEBUI: Add slow torrent options (Thomas Piccirello)
- WEBUI: Add "Use alternative Web UI" option (Thomas Piccirello)
- WEBUI: Add "Apply rate limit to peers on LAN" option (Thomas Piccirello)
- WEBUI: Add email "From" option (Thomas Piccirello)
- WEBUI: Set WebUI download options using set preferences (Thomas Piccirello)
- WEBUI: Show list of categories on WebUI download page (Thomas Piccirello)
- WEBUI: Hide WebUI text input for custom monitor save locations (Thomas Piccirello)
- WEBUI: Add "When adding a torrent" options (Thomas Piccirello)
- WEBUI: Add WebUI Auto TMM options (Thomas Piccirello)
- WEBUI: Add speed limit icons to WebUI Speed options (Thomas Piccirello)
- WEBUI: Add WebUI Random port button and proxy unencrypted password notice (Thomas Piccirello)
- WEBUI: Replace WebUI Options fixed-width labels (Thomas Piccirello)
- WEBUI: Reorder WebUI options to match GUI (Thomas Piccirello)
- WEBUI: Allow WebUI sidebar to be collapsed (Thomas Piccirello)
- WEBUI: Show ellipsis when WebUI sidebar is too narrow (Thomas Piccirello)
- WEBUI: Fix WebUI bug on override of Start Download option.Closes #9855. (Tom Piccirello)
- WEBUI: Fix missing words in WebUI (Chocobo1)
- WEBUI: Add SameSite attribute to WebUI session cookie (Thomas Piccirello)
- WEBUI: Put WebUI security related options into a groupbox (Chocobo1)
- WEBUI: Add option for WebUI Host header validation (Chocobo1)
- WEBUI: Show icon in WebUI sorted column (Thomas Piccirello)
- RSS: Keep track of REPACK/PROPER downloads. Closes #9898. (Stephen Dawkins)
- SEARCH: Only instantiate SearchPluginManager as needed (Thomas Piccirello)
- MACOS: Make file icon look like other macOS icons (Nick Korotysh)
- MACOS: Save option to start minimized in Mac (thalieht)
* Mon Nov 19 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.4
- FEATURE: Recognize *.ts files as previewable (silver)
- FEATURE: Allow to disable speed graphs (dzmat)

View File

@@ -1,9 +1,9 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2012-10-14.11; # UTC
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
@@ -17,7 +17,7 @@ scriptversion=2012-10-14.11; # UTC
# 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, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
@@ -255,7 +255,8 @@ EOF
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
@@ -339,9 +340,9 @@ exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

966
build-aux/config.guess vendored

File diff suppressed because it is too large Load Diff

2770
build-aux/config.sub vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2013-12-25.23; # UTC
scriptversion=2018-03-11.20; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
@@ -271,15 +271,18 @@ do
fi
dst=$dst_arg
# If destination is a directory, append the input filename; won't work
# if double slashes aren't ignored.
# If destination is a directory, append the input filename.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dst=$dstdir/`basename "$src"`
dstbase=`basename "$src"`
case $dst in
*/) dst=$dst$dstbase;;
*) dst=$dst/$dstbase;;
esac
dstdir_status=0
else
dstdir=`dirname "$dst"`
@@ -288,6 +291,11 @@ do
fi
fi
case $dstdir in
*/) dstdirslash=$dstdir;;
*) dstdirslash=$dstdir/;;
esac
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
@@ -324,34 +332,43 @@ do
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
;;
*)
# Note that $RANDOM variable is not portable (e.g. dash); Use it
# here however when possible just to lower collision chance.
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
# Because "mkdir -p" follows existing symlinks and we likely work
# directly in world-writeable /tmp, make sure that the '$tmpdir'
# directory is successfully created first before we actually test
# 'mkdir -p' feature.
if (umask $mkdir_umask &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
$mkdirprog $mkdir_mode "$tmpdir" &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
ls_ld_tmpdir=`ls -ld "$tmpdir"`
test_tmpdir="$tmpdir/a"
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/d" "$tmpdir"
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
fi
trap '' 0;;
esac;;
@@ -427,8 +444,8 @@ do
else
# Make a couple of temp file names in the proper directory.
dsttmp=$dstdir/_inst.$$_
rmtmp=$dstdir/_rm.$$_
dsttmp=${dstdirslash}_inst.$$_
rmtmp=${dstdirslash}_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
@@ -493,9 +510,9 @@ do
done
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

View File

@@ -1,9 +1,9 @@
#! /bin/sh
# Common wrapper for a few potentially missing GNU programs.
scriptversion=2013-10-28.13; # UTC
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
# Copyright (C) 1996-2018 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
@@ -17,7 +17,7 @@ scriptversion=2013-10-28.13; # UTC
# 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, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
@@ -101,9 +101,9 @@ else
exit $st
fi
perl_URL=http://www.perl.org/
flex_URL=http://flex.sourceforge.net/
gnu_software_URL=http://www.gnu.org/software
perl_URL=https://www.perl.org/
flex_URL=https://github.com/westes/flex
gnu_software_URL=https://www.gnu.org/software
program_details ()
{
@@ -207,9 +207,9 @@ give_advice "$1" | sed -e '1s/^/WARNING: /' \
exit $st
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

View File

@@ -5,6 +5,8 @@ BINDIR = @EXPAND_BINDIR@
DATADIR = @EXPAND_DATADIR@
MANPREFIX = @EXPAND_MANDIR@
QMAKE_CC = @QBT_CC@
QMAKE_CXX = @QBT_CXX@
QMAKE_CXXFLAGS += @QBT_CONF_EXTRA_CFLAGS@
EXTERNAL_INCLUDES = @QBT_CONF_INCLUDES@

483
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for qbittorrent v4.1.4.
# Generated by GNU Autoconf 2.69 for qbittorrent v4.1.9.
#
# Report bugs to <bugs.qbittorrent.org>.
#
@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='qbittorrent'
PACKAGE_TARNAME='qbittorrent'
PACKAGE_VERSION='v4.1.4'
PACKAGE_STRING='qbittorrent v4.1.4'
PACKAGE_VERSION='v4.1.9'
PACKAGE_STRING='qbittorrent v4.1.9'
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
PACKAGE_URL='https://www.qbittorrent.org/'
@@ -595,6 +595,8 @@ QBT_REMOVE_CONFIG
QBT_ADD_CONFIG
QBT_CONF_EXTRA_CFLAGS
QBT_CONF_INCLUDES
QBT_CXX
QBT_CC
EXPAND_MANDIR
EXPAND_DATADIR
EXPAND_BINDIR
@@ -626,7 +628,6 @@ am__nodep
AMDEPBACKSLASH
AMDEP_FALSE
AMDEP_TRUE
am__quote
am__include
DEPDIR
am__untar
@@ -709,7 +710,8 @@ PACKAGE_VERSION
PACKAGE_TARNAME
PACKAGE_NAME
PATH_SEPARATOR
SHELL'
SHELL
am__quote'
ac_subst_files=''
ac_user_opts='
enable_option_checking
@@ -1297,7 +1299,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures qbittorrent v4.1.4 to adapt to many kinds of systems.
\`configure' configures qbittorrent v4.1.9 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1368,7 +1370,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of qbittorrent v4.1.4:";;
short | recursive ) echo "Configuration of qbittorrent v4.1.9:";;
esac
cat <<\_ACEOF
@@ -1503,7 +1505,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
qbittorrent configure v4.1.4
qbittorrent configure v4.1.9
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1642,7 +1644,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by qbittorrent $as_me v4.1.4, which was
It was created by qbittorrent $as_me v4.1.9, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3274,7 +3276,7 @@ IFS=$ac_save_IFS
case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
am__api_version='1.15'
am__api_version='1.16'
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
@@ -3700,45 +3702,45 @@ DEPDIR="${am__leading_dot}deps"
ac_config_commands="$ac_config_commands depfiles"
am_make=${MAKE-make}
cat > confinc << 'END'
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; }
cat > confinc.mk << 'END'
am__doit:
@echo this is the am__doit target
@echo this is the am__doit target >confinc.out
.PHONY: am__doit
END
# If we don't find an include directive, just comment out the code.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
$as_echo_n "checking for style of include used by $am_make... " >&6; }
am__include="#"
am__quote=
_am_result=none
# First try GNU make style include.
echo "include confinc" > confmf
# Ignore all kinds of additional output from 'make'.
case `$am_make -s -f confmf 2> /dev/null` in #(
*the\ am__doit\ target*)
am__include=include
am__quote=
_am_result=GNU
;;
esac
# Now try BSD make style include.
if test "$am__include" = "#"; then
echo '.include "confinc"' > confmf
case `$am_make -s -f confmf 2> /dev/null` in #(
*the\ am__doit\ target*)
am__include=.include
am__quote="\""
_am_result=BSD
# BSD make does it like this.
echo '.include "confinc.mk" # ignored' > confmf.BSD
# Other make implementations (GNU, Solaris 10, AIX) do it like this.
echo 'include confinc.mk # ignored' > confmf.GNU
_am_result=no
for s in GNU BSD; do
{ echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
(${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }
case $?:`cat confinc.out 2>/dev/null` in #(
'0:this is the am__doit target') :
case $s in #(
BSD) :
am__include='.include' am__quote='"' ;; #(
*) :
am__include='include' am__quote='' ;;
esac ;; #(
*) :
;;
esac
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
$as_echo "$_am_result" >&6; }
rm -f confinc confmf
esac
if test "$am__include" != "#"; then
_am_result="yes ($s style)"
break
fi
done
rm -f confinc.* confmf.*
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
$as_echo "${_am_result}" >&6; }
# Check whether --enable-dependency-tracking was given.
if test "${enable_dependency_tracking+set}" = set; then :
@@ -3820,7 +3822,7 @@ fi
# Define the identity of the package.
PACKAGE='qbittorrent'
VERSION='v4.1.4'
VERSION='v4.1.9'
cat >>confdefs.h <<_ACEOF
@@ -3850,8 +3852,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
# For better backward compatibility. To be removed once Automake 1.9.x
# dies out for good. For more background, see:
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
mkdir_p='$(MKDIR_P)'
# We need awk for the "check" target (and possibly the TAP driver). The
@@ -4158,7 +4160,7 @@ END
Aborting the configuration process, to ensure you take notice of the issue.
You can download and install GNU coreutils to get an 'rm' implementation
that behaves properly: <http://www.gnu.org/software/coreutils/>.
that behaves properly: <https://www.gnu.org/software/coreutils/>.
If you want to complete the configuration process using your problematic
'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
@@ -4170,11 +4172,12 @@ END
fi
# use compiler from env variables if available
QBT_CC="$CC"
QBT_CXX="$CXX"
# Define --wth-* and --enable-* arguments
# Check whether --with-qtsingleapplication was given.
if test "${with_qtsingleapplication+set}" = set; then :
withval=$with_qtsingleapplication;
@@ -4745,7 +4748,9 @@ fi
case ${host_cpu} in #(
x86_64) :
libsubdirs="lib64 libx32 lib lib64" ;; #(
ppc64|s390x|sparc64|aarch64|ppc64le) :
mips*64*) :
libsubdirs="lib64 lib32 lib lib64" ;; #(
ppc64|powerpc64|s390x|sparc64|aarch64|ppc64le|powerpc64le|riscv64) :
libsubdirs="lib64 lib lib64" ;; #(
*) :
libsubdirs="lib"
@@ -4997,10 +5002,10 @@ $as_echo "$as_me: Your boost libraries seems to old (version $_version)." >&6;}
$as_echo "#define HAVE_BOOST /**/" >>confdefs.h
# execute ACTION-IF-FOUND (if present):
{ $as_echo "$as_me:${as_lineno-$LINENO}: Boost CPPFLAGS: \"$BOOST_CPPFLAGS\"
Boost LDFLAGS: \"$BOOST_LDFLAGS\"" >&5
$as_echo "$as_me: Boost CPPFLAGS: \"$BOOST_CPPFLAGS\"
Boost LDFLAGS: \"$BOOST_LDFLAGS\"" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: Boost CXXFLAGS: \"$BOOST_CPPFLAGS\"" >&5
$as_echo "$as_me: Boost CXXFLAGS: \"$BOOST_CPPFLAGS\"" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: Boost LDFLAGS: \"$BOOST_LDFLAGS\"" >&5
$as_echo "$as_me: Boost LDFLAGS: \"$BOOST_LDFLAGS\"" >&6;}
fi
CPPFLAGS="$CPPFLAGS_SAVED"
@@ -5011,16 +5016,10 @@ fi
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
CXXFLAGS="$BOOST_CPPFLAGS $CXXFLAGS"
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
# add workaround for problematic boost version
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
# taken from ax_boost_base.m4
@@ -5041,12 +5040,6 @@ else
QBT_ADD_DEFINES="$QBT_ADD_DEFINES BOOST_NO_CXX11_RVALUE_REFERENCES"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
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
@@ -5279,7 +5272,7 @@ fi
fi
if test "x$ax_lib" = "x"; then
as_fn_error $? "Could not find a version of the library!" "$LINENO" 5
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
@@ -5400,7 +5393,7 @@ else
libtorrent_LIBS=$pkg_cv_libtorrent_LIBS
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
CPPFLAGS="$libtorrent_CFLAGS $CPPFLAGS"
CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS"
LIBS="$libtorrent_LIBS $LIBS"
fi
@@ -5493,10 +5486,116 @@ else
zlib_LIBS=$pkg_cv_zlib_LIBS
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
CPPFLAGS="$zlib_CFLAGS $CPPFLAGS"
CXXFLAGS="$zlib_CFLAGS $CXXFLAGS"
LIBS="$zlib_LIBS $LIBS"
fi
# Check if already in >= C++11 mode because of the flags returned by one of the above packages
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler is using C++11 or later mode" >&5
$as_echo_n "checking if compiler is using C++11 or later mode... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
#endif
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
QBT_CXX11_FOUND="yes"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
QBT_CXX11_FOUND="no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
# In case of no, check if the compiler can support at least C++11
# and if yes, enable it leaving a warning to the user
if test "x$QBT_CXX11_FOUND" = "xno"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler supports C++11" >&5
$as_echo_n "checking if compiler supports C++11... " >&6; }
TMP_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS -std=c++11"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
#endif
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if C++11 is disabled by the set compiler flags" >&5
$as_echo_n "checking if C++11 is disabled by the set compiler flags... " >&6; }
# prepend the flag so it won't override conflicting user defined flags
CXXFLAGS="-std=c++11 $TMP_CXXFLAGS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
#endif
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
CXXFLAGS="$TMP_CXXFLAGS -std=c++11"
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: C++11 mode is now force enabled.
Make sure you use the same C++ mode for qBittorrent and its dependencies.
To explicitly set qBittorrent to a later mode use CXXFLAGS.
Example: \`CXXFLAGS=\"\$CXXFLAGS -std=c++14\" ./configure\`" >&5
$as_echo "$as_me: WARNING: C++11 mode is now force enabled.
Make sure you use the same C++ mode for qBittorrent and its dependencies.
To explicitly set qBittorrent to a later mode use CXXFLAGS.
Example: \`CXXFLAGS=\"\$CXXFLAGS -std=c++14\" ./configure\`" >&2;}
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
as_fn_error $? "The compiler supports C++11 but the user or a dependency has explicitly enabled a lower mode." "$LINENO" 5
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
as_fn_error $? "A compiler supporting C++11 is required." "$LINENO" 5
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
# These are required because autoconf doesn't expand these **particular**
# vars automatically. And qmake cannot autoexpand them.
@@ -5583,15 +5682,15 @@ extract() {
for i in $string; do
case "$(echo "$i" | cut -c1)" in
'') ;;
D) QBT_CONF_DEFINES="$(echo $i | cut -c2-) $QBT_CONF_DEFINES";;
I) QBT_CONF_INCLUDES="$(echo $i | cut -c2-) $QBT_CONF_INCLUDES";;
*) QBT_CONF_EXTRA_CFLAGS="-$i $QBT_CONF_EXTRA_CFLAGS";;
D) QBT_CONF_DEFINES="$QBT_CONF_DEFINES $(echo $i | cut -c2-)";;
I) QBT_CONF_INCLUDES="$QBT_CONF_INCLUDES $(echo $i | cut -c2-)";;
*) QBT_CONF_EXTRA_CFLAGS="$QBT_CONF_EXTRA_CFLAGS -$i";;
esac
done
IFS=$SAVEIFS
}
extract "$CFLAGS $CPPFLAGS $CXXFLAGS"
extract "$CFLAGS $CXXFLAGS"
QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
# Substitute the values of these vars in conf.pri.in
@@ -5602,6 +5701,8 @@ QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
ac_config_files="$ac_config_files conf.pri"
cat >confcache <<\_ACEOF
@@ -6174,7 +6275,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v4.1.4, which was
This file was extended by qbittorrent $as_me v4.1.9, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6232,7 +6333,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
qbittorrent config.status v4.1.4
qbittorrent config.status v4.1.9
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -6340,7 +6441,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
#
# INIT-COMMANDS
#
AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
_ACEOF
@@ -6785,29 +6886,35 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
# Older Autoconf quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
case $CONFIG_FILES in
*\'*) eval set x "$CONFIG_FILES" ;;
*) set x $CONFIG_FILES ;;
esac
# TODO: see whether this extra hack can be removed once we start
# requiring Autoconf 2.70 or later.
case $CONFIG_FILES in #(
*\'*) :
eval set x "$CONFIG_FILES" ;; #(
*) :
set x $CONFIG_FILES ;; #(
*) :
;;
esac
shift
for mf
# Used to flag and report bootstrapping failures.
am_rc=0
for am_mf
do
# Strip MF so we end up with the name of the file.
mf=`echo "$mf" | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile or not.
# We used to match only the files named 'Makefile.in', but
# some people rename them; so instead we look at the file content.
# Grep'ing the first line is not enough: some people post-process
# each Makefile.in and add a new line on top of each file to say so.
# Grep'ing the whole file is not good either: AIX grep has a line
am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile which includes
# dependency-tracking related rules and includes.
# Grep'ing the whole file directly is not great: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
dirpart=`$as_dirname -- "$mf" ||
$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$mf" : 'X\(//\)[^/]' \| \
X"$mf" : 'X\(//\)$' \| \
X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$mf" |
sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
|| continue
am_dirpart=`$as_dirname -- "$am_mf" ||
$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$am_mf" : 'X\(//\)[^/]' \| \
X"$am_mf" : 'X\(//\)$' \| \
X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$am_mf" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -6825,53 +6932,48 @@ $as_echo X"$mf" |
q
}
s/.*/./; q'`
else
continue
fi
# Extract the definition of DEPDIR, am__include, and am__quote
# from the Makefile without running 'make'.
DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
test -z "$DEPDIR" && continue
am__include=`sed -n 's/^am__include = //p' < "$mf"`
test -z "$am__include" && continue
am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
# Find all dependency output files, they are included files with
# $(DEPDIR) in their names. We invoke sed twice because it is the
# simplest approach to changing $(DEPDIR) to its actual value in the
# expansion.
for file in `sed -n "
s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
# Make sure the directory exists.
test -f "$dirpart/$file" && continue
fdir=`$as_dirname -- "$file" ||
$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$file" : 'X\(//\)[^/]' \| \
X"$file" : 'X\(//\)$' \| \
X"$file" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$file" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
am_filepart=`$as_basename -- "$am_mf" ||
$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
X"$am_mf" : 'X\(//\)$' \| \
X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X/"$am_mf" |
sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
/^X\/\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
/^X\/\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
as_dir=$dirpart/$fdir; as_fn_mkdir_p
# echo "creating $dirpart/$file"
echo '# dummy' > "$dirpart/$file"
done
{ echo "$as_me:$LINENO: cd "$am_dirpart" \
&& sed -e '/# am--include-marker/d' "$am_filepart" \
| $MAKE -f - am--depfiles" >&5
(cd "$am_dirpart" \
&& sed -e '/# am--include-marker/d' "$am_filepart" \
| $MAKE -f - am--depfiles) >&5 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } || am_rc=$?
done
if test $am_rc -ne 0; then
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "Something went wrong bootstrapping makefile fragments
for automatic dependency tracking. Try re-running configure with the
'--disable-dependency-tracking' option to at least be able to build
the package (albeit without support for automatic dependency tracking).
See \`config.log' for more details" "$LINENO" 5; }
fi
{ am_dirpart=; unset am_dirpart;}
{ am_filepart=; unset am_filepart;}
{ am_mf=; unset am_mf;}
{ am_rc=; unset am_rc;}
rm -f conftest-deps.mk
}
;;
@@ -7489,7 +7591,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v4.1.4, which was
This file was extended by qbittorrent $as_me v4.1.9, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7547,7 +7649,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
qbittorrent config.status v4.1.4
qbittorrent config.status v4.1.9
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -7655,7 +7757,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
#
# INIT-COMMANDS
#
AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
_ACEOF
@@ -8101,29 +8203,35 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
# Older Autoconf quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
case $CONFIG_FILES in
*\'*) eval set x "$CONFIG_FILES" ;;
*) set x $CONFIG_FILES ;;
esac
# TODO: see whether this extra hack can be removed once we start
# requiring Autoconf 2.70 or later.
case $CONFIG_FILES in #(
*\'*) :
eval set x "$CONFIG_FILES" ;; #(
*) :
set x $CONFIG_FILES ;; #(
*) :
;;
esac
shift
for mf
# Used to flag and report bootstrapping failures.
am_rc=0
for am_mf
do
# Strip MF so we end up with the name of the file.
mf=`echo "$mf" | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile or not.
# We used to match only the files named 'Makefile.in', but
# some people rename them; so instead we look at the file content.
# Grep'ing the first line is not enough: some people post-process
# each Makefile.in and add a new line on top of each file to say so.
# Grep'ing the whole file is not good either: AIX grep has a line
am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile which includes
# dependency-tracking related rules and includes.
# Grep'ing the whole file directly is not great: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
dirpart=`$as_dirname -- "$mf" ||
$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$mf" : 'X\(//\)[^/]' \| \
X"$mf" : 'X\(//\)$' \| \
X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$mf" |
sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
|| continue
am_dirpart=`$as_dirname -- "$am_mf" ||
$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$am_mf" : 'X\(//\)[^/]' \| \
X"$am_mf" : 'X\(//\)$' \| \
X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$am_mf" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -8141,53 +8249,48 @@ $as_echo X"$mf" |
q
}
s/.*/./; q'`
else
continue
fi
# Extract the definition of DEPDIR, am__include, and am__quote
# from the Makefile without running 'make'.
DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
test -z "$DEPDIR" && continue
am__include=`sed -n 's/^am__include = //p' < "$mf"`
test -z "$am__include" && continue
am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
# Find all dependency output files, they are included files with
# $(DEPDIR) in their names. We invoke sed twice because it is the
# simplest approach to changing $(DEPDIR) to its actual value in the
# expansion.
for file in `sed -n "
s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
# Make sure the directory exists.
test -f "$dirpart/$file" && continue
fdir=`$as_dirname -- "$file" ||
$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$file" : 'X\(//\)[^/]' \| \
X"$file" : 'X\(//\)$' \| \
X"$file" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X"$file" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
am_filepart=`$as_basename -- "$am_mf" ||
$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
X"$am_mf" : 'X\(//\)$' \| \
X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
$as_echo X/"$am_mf" |
sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
/^X\/\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
/^X\/\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
as_dir=$dirpart/$fdir; as_fn_mkdir_p
# echo "creating $dirpart/$file"
echo '# dummy' > "$dirpart/$file"
done
{ echo "$as_me:$LINENO: cd "$am_dirpart" \
&& sed -e '/# am--include-marker/d' "$am_filepart" \
| $MAKE -f - am--depfiles" >&5
(cd "$am_dirpart" \
&& sed -e '/# am--include-marker/d' "$am_filepart" \
| $MAKE -f - am--depfiles) >&5 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } || am_rc=$?
done
if test $am_rc -ne 0; then
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
as_fn_error $? "Something went wrong bootstrapping makefile fragments
for automatic dependency tracking. Try re-running configure with the
'--disable-dependency-tracking' option to at least be able to build
the package (albeit without support for automatic dependency tracking).
See \`config.log' for more details" "$LINENO" 5; }
fi
{ am_dirpart=; unset am_dirpart;}
{ am_filepart=; unset am_filepart;}
{ am_mf=; unset am_mf;}
{ am_rc=; unset am_rc;}
rm -f conftest-deps.mk
}
;;

View File

@@ -1,4 +1,4 @@
AC_INIT([qbittorrent], [v4.1.4], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_INIT([qbittorrent], [v4.1.9], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
@@ -8,7 +8,9 @@ AC_LANG(C++)
AC_CANONICAL_HOST
AM_INIT_AUTOMAKE
# use compiler from env variables if available
QBT_CC="$CC"
QBT_CXX="$CXX"
# Define --wth-* and --enable-* arguments
@@ -162,14 +164,13 @@ AS_CASE(["x$enable_qt_dbus"],
AX_BOOST_BASE([1.35],
[AC_MSG_NOTICE([Boost CPPFLAGS: "$BOOST_CPPFLAGS"
Boost LDFLAGS: "$BOOST_LDFLAGS"])],
[AC_MSG_NOTICE([Boost CXXFLAGS: "$BOOST_CPPFLAGS"])
AC_MSG_NOTICE([Boost LDFLAGS: "$BOOST_LDFLAGS"])],
[AC_MSG_ERROR([Could not find Boost])])
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
CXXFLAGS="$BOOST_CPPFLAGS $CXXFLAGS"
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
# add workaround for problematic boost version
AC_LANG_PUSH(C++)
# taken from ax_boost_base.m4
m4_define([DETECT_BOOST_VERSION_PROGRAM],
[AC_LANG_PROGRAM([[#include <boost/version.hpp>]],
@@ -177,7 +178,6 @@ 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"])
AC_LANG_POP([C++])
AX_BOOST_SYSTEM()
AC_MSG_NOTICE([Boost.System LIB: "$BOOST_SYSTEM_LIB"])
@@ -196,14 +196,46 @@ AS_CASE(["x$with_qtsingleapplication"],
PKG_CHECK_MODULES(libtorrent,
[libtorrent-rasterbar >= 1.0.6],
[CPPFLAGS="$libtorrent_CFLAGS $CPPFLAGS"
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS"
LIBS="$libtorrent_LIBS $LIBS"])
PKG_CHECK_MODULES(zlib,
[zlib >= 1.2.5.2],
[CPPFLAGS="$zlib_CFLAGS $CPPFLAGS"
[CXXFLAGS="$zlib_CFLAGS $CXXFLAGS"
LIBS="$zlib_LIBS $LIBS"])
# Check if already in >= C++11 mode because of the flags returned by one of the above packages
AC_MSG_CHECKING([if compiler is using C++11 or later mode])
AC_COMPILE_IFELSE([DETECT_CPP11_PROGRAM()],
[AC_MSG_RESULT([yes])
QBT_CXX11_FOUND="yes"],
[AC_MSG_RESULT([no])
QBT_CXX11_FOUND="no"])
# In case of no, check if the compiler can support at least C++11
# and if yes, enable it leaving a warning to the user
AS_IF([test "x$QBT_CXX11_FOUND" = "xno"],
[AC_MSG_CHECKING([if compiler supports C++11])
TMP_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="$CXXFLAGS -std=c++11"
AC_COMPILE_IFELSE([DETECT_CPP11_PROGRAM()],
[AC_MSG_RESULT([yes])
AC_MSG_CHECKING([if C++11 is disabled by the set compiler flags])
# prepend the flag so it won't override conflicting user defined flags
CXXFLAGS="-std=c++11 $TMP_CXXFLAGS"
AC_COMPILE_IFELSE([DETECT_CPP11_PROGRAM()],
[AC_MSG_RESULT([no])
CXXFLAGS="$TMP_CXXFLAGS -std=c++11"
AC_MSG_WARN([C++11 mode is now force enabled.
Make sure you use the same C++ mode for qBittorrent and its dependencies.
To explicitly set qBittorrent to a later mode use CXXFLAGS.
Example: `CXXFLAGS="\$CXXFLAGS -std=c++14" ./configure`])],
[AC_MSG_RESULT([yes])
AC_MSG_ERROR([The compiler supports C++11 but the user or a dependency has explicitly enabled a lower mode.])])],
[AC_MSG_RESULT([no])
AC_MSG_ERROR([A compiler supporting C++11 is required.])])
])
# These are required because autoconf doesn't expand these **particular**
# vars automatically. And qmake cannot autoexpand them.
AX_DEFINE_DIR([EXPAND_PREFIX], [prefix])
@@ -230,18 +262,20 @@ extract() {
for i in $string; do
case "$(echo "$i" | cut -c1)" in
'') ;;
D) QBT_CONF_DEFINES="$(echo $i | cut -c2-) $QBT_CONF_DEFINES";;
I) QBT_CONF_INCLUDES="$(echo $i | cut -c2-) $QBT_CONF_INCLUDES";;
*) QBT_CONF_EXTRA_CFLAGS="-$i $QBT_CONF_EXTRA_CFLAGS";;
D) QBT_CONF_DEFINES="$QBT_CONF_DEFINES $(echo $i | cut -c2-)";;
I) QBT_CONF_INCLUDES="$QBT_CONF_INCLUDES $(echo $i | cut -c2-)";;
*) QBT_CONF_EXTRA_CFLAGS="$QBT_CONF_EXTRA_CFLAGS -$i";;
esac
done
IFS=$SAVEIFS
}
extract "$CFLAGS $CPPFLAGS $CXXFLAGS"
extract "$CFLAGS $CXXFLAGS"
QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
# Substitute the values of these vars in conf.pri.in
AC_SUBST(QBT_CC)
AC_SUBST(QBT_CXX)
AC_SUBST(QBT_CONF_INCLUDES)
AC_SUBST(QBT_CONF_EXTRA_CFLAGS)
AC_SUBST(QBT_ADD_CONFIG)

22
dist/mac/Info.plist vendored
View File

@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>qBittorrent</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
@@ -21,6 +25,10 @@
<array>
<string>org.bittorrent.torrent</string>
</array>
<key>NSExportableTypes</key>
<array>
<string>org.bittorrent.torrent</string>
</array>
<key>LSIsAppleDefaultForType</key>
<true/>
</dict>
@@ -28,6 +36,8 @@
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLSchemes</key>
<array>
<string>magnet</string>
@@ -45,21 +55,19 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.1.4</string>
<key>CFBundleSignature</key>
<string>qBit</string>
<string>4.1.9</string>
<key>CFBundleExecutable</key>
<string>@EXECUTABLE@</string>
<key>CFBundleIdentifier</key>
<string>org.qbittorrent</string>
<string>org.qbittorrent.qBittorrent</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}.0</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<string>True</string>
<key>NSAppleScriptEnabled</key>
<string>YES</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2006-2018 The qBittorrent project</string>
<string>Copyright © 2006-2019 The qBittorrent project</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>

Binary file not shown.

View File

@@ -31,12 +31,12 @@ if (Qt5Widgets_FOUND)
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
FILES_MATCHING PATTERN "*.png")
install(FILES qbittorrent.desktop
install(FILES org.qbittorrent.qBittorrent.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
COMPONENT data)
install(FILES qbittorrent.appdata.xml
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/appdata/
install(FILES org.qbittorrent.qBittorrent.appdata.xml
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo/
COMPONENT data)
install(FILES

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2014 sledgehammer999 <sledgehammer999@qbittorrent.org> -->
<component type="desktop">
<id>org.qbittorrent.qBittorrent</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0 and OpenSSL</project_license>
<name>qBittorrent</name>
<summary>An open-source Bittorrent client</summary>
<description>
<p>
The qBittorrent project aims to provide an open-source software alternative to µTorrent.
Additionally, qBittorrent runs and provides the same features on all major platforms (FreeBSD, Linux, macOS, OS/2, Windows).
qBittorrent is based on the Qt toolkit and libtorrent-rasterbar library.
</p>
<ul>
<li>Polished µTorrent-like User Interface</li>
<li>
Well-integrated and extensible Search Engine
<ul>
<li>Simultaneous search in many Torrent search sites</li>
<li>Category-specific search requests (e.g. Books, Music, Software)</li>
</ul>
</li>
<li>RSS feed support with advanced download filters (incl. regex)</li>
<li>
Many Bittorrent extensions supported:
<ul>
<li>Magnet links</li>
<li>Distributed hash table (DHT), peer exchange protocol (PEX), local peer discovery (LSD)</li>
<li>Private torrents</li>
<li>Encrypted connections</li>
<li>and many more...</li>
</ul>
</li>
<li>Remote control through Web user interface, written with AJAX</li>
<li>Sequential downloading (Download in order)</li>
<li>
Advanced control over torrents, trackers and peers
<ul>
<li>Torrents queueing and prioritizing</li>
<li>Torrent content selection and prioritizing</li>
</ul>
</li>
<li>Bandwidth scheduler</li>
<li>Torrent creation tool</li>
<li>IP Filtering (eMule &amp; PeerGuardian format compatible)</li>
<li>IPv6 compliant</li>
<li>UPnP / NAT-PMP port forwarding support</li>
<li>Available on all platforms: Windows, Linux, macOS, FreeBSD, OS/2</li>
<li>Available in ~70 languages</li>
</ul>
</description>
<launchable type="desktop-id">org.qbittorrent.qBittorrent.desktop</launchable>
<screenshots>
<screenshot type="default">
<image height="675" width="1200">https://alexpl.fedorapeople.org/AppData/qbittorrent/screens/qbittorrent_01.png</image>
</screenshot>
<screenshot>
<image height="675" width="1200">https://alexpl.fedorapeople.org/AppData/qbittorrent/screens/qbittorrent_02.png</image>
</screenshot>
<screenshot>
<image height="675" width="1200">https://alexpl.fedorapeople.org/AppData/qbittorrent/screens/qbittorrent_03.png</image>
</screenshot>
<screenshot>
<image height="675" width="1200">https://alexpl.fedorapeople.org/AppData/qbittorrent/screens/qbittorrent_04.png</image>
</screenshot>
</screenshots>
<update_contact>sledgehammer999@qbittorrent.org</update_contact>
<developer_name>The qBittorrent Project</developer_name>
<url type="homepage">https://www.qbittorrent.org/</url>
<url type="bugtracker">http://bugs.qbittorrent.org/</url>
<url type="donation">https://www.qbittorrent.org/donate</url>
<url type="help">http://forum.qbittorrent.org/</url>
<url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url>
</component>

View File

@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2014 sledgehammer999 <sledgehammer999@qbittorrent.org> -->
<component type="desktop">
<id>qbittorrent.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0 and OpenSSL</project_license>
<name>qBittorrent</name>
<summary>A Bittorrent Client</summary>
<description>
<p>
Aiming to be a good alternative to all other bittorrent clients out
there, qBittorrent is fast, stable and provides unicode support as well
as many other features. Additionally, qBittorrent runs and provides those
same features on all major platforms (Linux, Mac OS X, Windows, FreeBSD).
</p>
<p>
It is programmed in C++ / Qt and uses libtorrent (sometimes called
libtorrent-rasterbar) by Arvid Norberg. GeoLite data, created by MaxMind,
are included in qBittorrent. Its features include:
</p>
<ul>
<li>Polished µTorrent-like User Interface</li>
<li>Well-integrated and extensible Search Engine</li>
<li>All Bittorrent extensions (DHT, Peer Exchange, Full encryption, Magnet/BitComet URIs, ...)</li>
<li>Remote control through a Web user interface</li>
<li>Advanced control over trackers, peers and torrents</li>
<li>UPnP / NAT-PMP port forwarding support</li>
<li>Available in ~25 languages (Unicode support)</li>
<li>Torrent creation tool</li>
<li>Advanced RSS support with download filters (inc. regex)</li>
<li>Bandwidth scheduler</li>
<li>IP Filtering (eMule and PeerGuardian compatible)</li>
<li>IPv6 compliant</li>
<li>Sequential downloading (aka "Download in order")</li>
</ul>
</description>
<screenshots>
<screenshot type="default">
<image height="675" width="1200">
https://alexpl.fedorapeople.org/AppData/qbittorrent/screens/qbittorrent_01.png
</image>
</screenshot>
<screenshot>
<image height="675" width="1200">
https://alexpl.fedorapeople.org/AppData/qbittorrent/screens/qbittorrent_02.png
</image>
</screenshot>
<screenshot>
<image height="675" width="1200">
https://alexpl.fedorapeople.org/AppData/qbittorrent/screens/qbittorrent_03.png
</image>
</screenshot>
<screenshot>
<image height="675" width="1200">
https://alexpl.fedorapeople.org/AppData/qbittorrent/screens/qbittorrent_04.png
</image>
</screenshot>
</screenshots>
<url type="homepage">https://www.qbittorrent.org/</url>
<update_contact>sledgehammer999@qbittorrent.org</update_contact>
<developer_name>The qBittorrent Project</developer_name>
<url type="bugtracker">http://bugs.qbittorrent.org/</url>
<url type="donation">https://www.qbittorrent.org/donate</url>
<url type="help">http://forum.qbittorrent.org/</url>
<url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url>
</component>

View File

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

View File

@@ -33,7 +33,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 42
#serial 47
# example boost program (need to pass version)
m4_define([_AX_BOOST_BASE_PROGRAM],
@@ -113,7 +113,8 @@ AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[
dnl are found, e.g. when only header-only libraries are installed!
AS_CASE([${host_cpu}],
[x86_64],[libsubdirs="lib64 libx32 lib lib64"],
[ppc64|s390x|sparc64|aarch64|ppc64le],[libsubdirs="lib64 lib lib64"],
[mips*64*],[libsubdirs="lib64 lib32 lib lib64"],
[ppc64|powerpc64|s390x|sparc64|aarch64|ppc64le|powerpc64le|riscv64],[libsubdirs="lib64 lib lib64"],
[libsubdirs="lib"]
)

View File

@@ -31,7 +31,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 19
#serial 20
AC_DEFUN([AX_BOOST_SYSTEM],
[
@@ -108,7 +108,7 @@ AC_DEFUN([AX_BOOST_SYSTEM],
fi
if test "x$ax_lib" = "x"; then
AC_MSG_ERROR(Could not find a version of the library!)
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 !)

View File

@@ -36,3 +36,16 @@ AC_DEFUN([FIND_QTDBUS],
[AC_MSG_RESULT([not found])
HAVE_QTDBUS=[false]])
])
# DETECT_CPP11_PROGRAM()
# Detects if at least C++11 mode is enabled.
# --------------------------------------
AC_DEFUN([DETECT_CPP11_PROGRAM],
[AC_LANG_PROGRAM([[
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
#endif]],
[[]])
])

View File

@@ -44,6 +44,7 @@
#endif
#ifndef DISABLE_GUI
#include <QMessageBox>
#ifdef Q_OS_WIN
#include <QSessionManager>
#include <QSharedMemory>
@@ -61,6 +62,7 @@
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/exceptions.h"
#include "base/iconprovider.h"
#include "base/logger.h"
#include "base/net/downloadmanager.h"
@@ -122,6 +124,7 @@ Application::Application(const QString &id, int &argc, char **argv)
qRegisterMetaType<Log::Msg>("Log::Msg");
setApplicationName("qBittorrent");
setOrganizationDomain("qbittorrent.org");
validateCommandLineParameters();
QString profileDir = m_commandLineArgs.portableMode
@@ -143,6 +146,9 @@ Application::Application(const QString &id, int &argc, char **argv)
#if !defined(DISABLE_GUI)
setAttribute(Qt::AA_UseHighDpiPixmaps, true); // opt-in to the high DPI pixmap support
setQuitOnLastWindowClosed(false);
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
setDesktopFileName("org.qbittorrent.qBittorrent");
#endif
#endif
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
@@ -326,7 +332,11 @@ void Application::runExternalProgram(const BitTorrent::TorrentHandle *torrent) c
::LocalFree(args);
#else
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
// Cannot give users shell environment by default, as doing so could
// enable command injection via torrent name and other arguments
// (especially when some automated download mechanism has been setup).
// See: https://github.com/qbittorrent/qBittorrent/issues/10925
QProcess::startDetached(program);
#endif
}
@@ -428,7 +438,7 @@ void Application::processParams(const QStringList &params)
BitTorrent::AddTorrentParams torrentParams;
TriStateBool skipTorrentDialog;
foreach (QString param, params) {
for (QString param : params) {
param = param.trimmed();
// Process strings indicating options specified by the user.
@@ -495,27 +505,42 @@ int Application::exec(const QStringList &params)
GuiIconProvider::initInstance();
#endif
BitTorrent::Session::initInstance();
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentFinished, this, &Application::torrentFinished);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::allTorrentsFinished, this, &Application::allTorrentsFinished, Qt::QueuedConnection);
try {
BitTorrent::Session::initInstance();
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentFinished, this, &Application::torrentFinished);
connect(BitTorrent::Session::instance(), &BitTorrent::Session::allTorrentsFinished, this, &Application::allTorrentsFinished, Qt::QueuedConnection);
#ifndef DISABLE_COUNTRIES_RESOLUTION
Net::GeoIPManager::initInstance();
Net::GeoIPManager::initInstance();
#endif
ScanFoldersModel::initInstance(this);
ScanFoldersModel::initInstance(this);
#ifndef DISABLE_WEBUI
m_webui = new WebUI;
m_webui = new WebUI;
#ifdef DISABLE_GUI
if (m_webui->isErrored())
return 1;
connect(m_webui, &WebUI::fatalError, this, []() { QCoreApplication::exit(1); });
if (m_webui->isErrored())
return 1;
connect(m_webui, &WebUI::fatalError, this, []() { QCoreApplication::exit(1); });
#endif // DISABLE_GUI
#endif // DISABLE_WEBUI
new RSS::Session; // create RSS::Session singleton
new RSS::AutoDownloader; // create RSS::AutoDownloader singleton
new SearchPluginManager;
new RSS::Session; // create RSS::Session singleton
new RSS::AutoDownloader; // create RSS::AutoDownloader singleton
}
catch (const RuntimeError &err) {
#ifdef DISABLE_GUI
fprintf(stderr, "%s", err.what());
#else
QMessageBox msgBox;
msgBox.setIcon(QMessageBox::Critical);
msgBox.setText(tr("Application failed to start."));
msgBox.setInformativeText(err.message());
msgBox.show(); // Need to be shown or to moveToCenter does not work
msgBox.move(Utils::Misc::screenCenter(&msgBox));
msgBox.exec();
#endif
return 1;
}
#ifdef DISABLE_GUI
#ifndef DISABLE_WEBUI
@@ -710,7 +735,6 @@ void Application::cleanup()
delete m_webui;
#endif
delete SearchPluginManager::instance();
delete RSS::AutoDownloader::instance();
delete RSS::Session::instance();
@@ -726,6 +750,7 @@ void Application::cleanup()
delete m_fileLogger;
Logger::freeInstance();
IconProvider::freeInstance();
SearchPluginManager::freeInstance();
Utils::Fs::removeDirRecursive(Utils::Fs::tempPath());
#ifndef DISABLE_GUI

View File

@@ -41,6 +41,7 @@
#include <QMessageBox>
#endif
#include "base/global.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
@@ -486,7 +487,7 @@ CommandLineParameterError::CommandLineParameterError(const QString &messageForUs
{
}
const QString& CommandLineParameterError::messageForUser() const
const QString &CommandLineParameterError::messageForUser() const
{
return m_messageForUser;
}
@@ -497,7 +498,7 @@ QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN
QStringList lines = {words.first()};
int currentLineMaxLength = wrapAtColumn - initialIndentation;
foreach (const QString &word, words.mid(1)) {
for (const QString &word : asConst(words.mid(1))) {
if (lines.last().length() + word.length() + 1 < currentLineMaxLength) {
lines.last().append(' ' + word);
}

View File

@@ -33,6 +33,7 @@
#include <QFile>
#include <QTextStream>
#include "base/global.h"
#include "base/logger.h"
#include "base/utils/fs.h"
@@ -50,7 +51,7 @@ FileLogger::FileLogger(const QString &path, const bool backup, const int maxSize
this->deleteOld(age, ageType);
const Logger *const logger = Logger::instance();
foreach (const Log::Msg &msg, logger->getMessages())
for (const Log::Msg &msg : asConst(logger->getMessages()))
addLogMessage(msg);
connect(logger, &Logger::newLogMessage, this, &FileLogger::addLogMessage);
@@ -87,7 +88,7 @@ void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
QDateTime date = QDateTime::currentDateTime();
QDir dir(Utils::Fs::branchPath(m_path));
foreach (const QFileInfo file, dir.entryInfoList(QStringList("qbittorrent.log.bak*"), QDir::Files | QDir::Writable, QDir::Time | QDir::Reversed)) {
for (const QFileInfo &file : asConst(dir.entryInfoList(QStringList("qbittorrent.log.bak*"), QDir::Files | QDir::Writable, QDir::Time | QDir::Reversed))) {
QDateTime modificationDate = file.lastModified();
switch (ageType) {
case DAYS:

View File

@@ -202,7 +202,7 @@ int main(int argc, char *argv[])
// this is the default in Qt6
app->setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif
#endif
#endif // Q_OS_WIN
#if defined(Q_OS_MAC)
// Since Apple made difficult for users to set PATH, we set here for convenience.

View File

@@ -101,6 +101,7 @@ QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
#endif
server = new QLocalServer(this);
server->setSocketOptions(QLocalServer::UserAccessOption);
QString lockName = QDir(QDir::tempPath()).absolutePath()
+ QLatin1Char('/') + socketName
+ QLatin1String("-lockfile");
@@ -191,6 +192,12 @@ void QtLocalPeer::receiveConnection()
QByteArray uMsg;
quint32 remaining;
ds >> remaining;
if (remaining > 65535) {
// drop suspiciously large data
delete socket;
return;
}
uMsg.resize(remaining);
int got = 0;
char* uMsgBuf = uMsg.data();

View File

@@ -83,7 +83,7 @@ bool userAcceptsUpgrade()
msgBox.move(Utils::Misc::screenCenter(&msgBox));
if (msgBox.exec() == QMessageBox::Ok)
return true;
#endif
#endif // DISABLE_GUI
return false;
}
@@ -179,9 +179,9 @@ bool upgrade(bool ask = true)
// ****************************************************************************************
// Silently converts old v3.3.x .fastresume files
QStringList backupFiles_3_3 = backupFolderDir.entryList(
const QStringList backupFiles_3_3 = backupFolderDir.entryList(
QStringList(QLatin1String("*.fastresume.*")), QDir::Files, QDir::Unsorted);
foreach (const QString &backupFile, backupFiles_3_3)
for (const QString &backupFile : backupFiles_3_3)
upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile));
// ****************************************************************************************
@@ -197,10 +197,10 @@ bool upgrade(bool ask = true)
if (ask && !userAcceptsUpgrade()) return false;
QStringList backupFiles = backupFolderDir.entryList(
const QStringList backupFiles = backupFolderDir.entryList(
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
const QRegularExpression rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
foreach (QString backupFile, backupFiles) {
for (const QString &backupFile : backupFiles) {
const QRegularExpressionMatch rxMatch = rx.match(backupFile);
if (rxMatch.hasMatch()) {
const QString hashStr = rxMatch.captured(1);
@@ -265,7 +265,7 @@ void migratePlistToIni(const QString &application)
plistFile->setFallbacksEnabled(false);
const QStringList plist = plistFile->allKeys();
if (!plist.isEmpty()) {
foreach (const QString &key, plist)
for (const QString &key : plist)
iniFile.setValue(key, plistFile->value(key));
plistFile->clear();
}

View File

@@ -4,6 +4,7 @@ add_library(qbt_base STATIC
# headers
bittorrent/addtorrentparams.h
bittorrent/cachestatus.h
bittorrent/filepriority.h
bittorrent/infohash.h
bittorrent/magneturi.h
bittorrent/peerinfo.h
@@ -76,6 +77,7 @@ types.h
unicodestrings.h
# sources
bittorrent/filepriority.cpp
bittorrent/infohash.cpp
bittorrent/magneturi.cpp
bittorrent/peerinfo.cpp

View File

@@ -38,12 +38,12 @@ AsyncFileStorage::AsyncFileStorage(const QString &storageFolderPath, QObject *pa
, m_lockFile(m_storageDir.absoluteFilePath(QStringLiteral("storage.lock")))
{
if (!m_storageDir.mkpath(m_storageDir.absolutePath()))
throw AsyncFileStorageError(
QString("Could not create directory '%1'.").arg(m_storageDir.absolutePath()));
throw AsyncFileStorageError {tr("Could not create directory '%1'.")
.arg(m_storageDir.absolutePath())};
// TODO: This folder locking approach does not work for UNIX systems. Implement it.
if (!m_lockFile.open(QFile::WriteOnly))
throw AsyncFileStorageError(m_lockFile.errorString());
throw AsyncFileStorageError {m_lockFile.errorString()};
}
AsyncFileStorage::~AsyncFileStorage()
@@ -76,13 +76,3 @@ void AsyncFileStorage::store_impl(const QString &fileName, const QByteArray &dat
}
}
}
AsyncFileStorageError::AsyncFileStorageError(const QString &message)
: std::runtime_error(message.toUtf8().data())
{
}
QString AsyncFileStorageError::message() const
{
return what();
}

View File

@@ -28,22 +28,22 @@
#pragma once
#include <stdexcept>
#include <QDir>
#include <QFile>
#include <QObject>
class AsyncFileStorageError : public std::runtime_error
#include "base/exceptions.h"
class AsyncFileStorageError : public RuntimeError
{
public:
explicit AsyncFileStorageError(const QString &message);
QString message() const;
using RuntimeError::RuntimeError;
};
class AsyncFileStorage : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(AsyncFileStorage)
public:
explicit AsyncFileStorage(const QString &storageFolderPath, QObject *parent = nullptr);

View File

@@ -3,6 +3,7 @@ HEADERS += \
$$PWD/asyncfilestorage.h \
$$PWD/bittorrent/addtorrentparams.h \
$$PWD/bittorrent/cachestatus.h \
$$PWD/bittorrent/filepriority.h \
$$PWD/bittorrent/infohash.h \
$$PWD/bittorrent/magneturi.h \
$$PWD/bittorrent/peerinfo.h \
@@ -75,6 +76,7 @@ HEADERS += \
SOURCES += \
$$PWD/asyncfilestorage.cpp \
$$PWD/bittorrent/filepriority.cpp \
$$PWD/bittorrent/infohash.cpp \
$$PWD/bittorrent/magneturi.cpp \
$$PWD/bittorrent/peerinfo.cpp \

View File

@@ -0,0 +1,46 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "filepriority.h"
namespace BitTorrent
{
bool isValidFilePriority(const BitTorrent::FilePriority priority)
{
switch (priority) {
case BitTorrent::FilePriority::Ignored:
case BitTorrent::FilePriority::Normal:
case BitTorrent::FilePriority::High:
case BitTorrent::FilePriority::Maximum:
case BitTorrent::FilePriority::Mixed:
return true;
default:
return false;
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
namespace BitTorrent
{
enum class FilePriority : int
{
Ignored = 0,
Normal = 1,
High = 6,
Maximum = 7,
Mixed = -1
};
bool isValidFilePriority(BitTorrent::FilePriority priority);
}

View File

@@ -83,10 +83,10 @@ MagnetUri::MagnetUri(const QString &source)
m_hash = m_addTorrentParams.info_hash;
m_name = QString::fromStdString(m_addTorrentParams.name);
foreach (const std::string &tracker, m_addTorrentParams.trackers)
for (const std::string &tracker : m_addTorrentParams.trackers)
m_trackers.append(QString::fromStdString(tracker));
foreach (const std::string &urlSeed, m_addTorrentParams.url_seeds)
for (const std::string &urlSeed : m_addTorrentParams.url_seeds)
m_urlSeeds.append(QUrl(urlSeed.c_str()));
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* 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
@@ -39,18 +39,23 @@ ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath
{
}
void ResumeDataSavingManager::saveResumeData(QString infoHash, QByteArray data) const
void ResumeDataSavingManager::save(const QString &filename, const QByteArray &data) const
{
QString filename = QString("%1.fastresume").arg(infoHash);
QString filepath = m_resumeDataDir.absoluteFilePath(filename);
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
qDebug() << "Saving resume data in" << filepath;
QSaveFile resumeFile(filepath);
if (resumeFile.open(QIODevice::WriteOnly)) {
resumeFile.write(data);
if (!resumeFile.commit()) {
Logger::instance()->addMessage(QString("Couldn't save resume data in %1. Error: %2")
.arg(filepath, resumeFile.errorString()), Log::WARNING);
QSaveFile file {filepath};
if (file.open(QIODevice::WriteOnly)) {
file.write(data);
if (!file.commit()) {
Logger::instance()->addMessage(QString("Couldn't save data in '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::WARNING);
}
}
}
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) 2015 Vladimir Golovnev <glassez@yandex.ru>
* 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
@@ -26,8 +26,7 @@
* exception statement from your version.
*/
#ifndef RESUMEDATASAVINGMANAGER_H
#define RESUMEDATASAVINGMANAGER_H
#pragma once
#include <QByteArray>
#include <QDir>
@@ -36,15 +35,15 @@
class ResumeDataSavingManager : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(ResumeDataSavingManager)
public:
explicit ResumeDataSavingManager(const QString &resumeFolderPath);
public slots:
void saveResumeData(QString infoHash, QByteArray data) const;
void save(const QString &filename, const QByteArray &data) const;
void remove(const QString &filename) const;
private:
QDir m_resumeDataDir;
};
#endif // RESUMEDATASAVINGMANAGER_H

File diff suppressed because it is too large Load Diff

View File

@@ -237,7 +237,7 @@ namespace BitTorrent
int diskJobTime = 0;
} disk;
};
#endif
#endif // LIBTORRENT_VERSION_NUM >= 10100
class Session : public QObject
{
@@ -377,6 +377,8 @@ namespace BitTorrent
void setAnnounceToAllTiers(bool val);
int asyncIOThreads() const;
void setAsyncIOThreads(int num);
int checkingMemUsage() const;
void setCheckingMemUsage(int size);
int diskCacheSize() const;
void setDiskCacheSize(int size);
int diskCacheTTL() const;
@@ -480,8 +482,8 @@ namespace BitTorrent
void bottomTorrentsPriority(const QStringList &hashes);
// TorrentHandle interface
void handleTorrentSaveResumeDataRequested(TorrentHandle *const torrent);
void handleTorrentShareLimitChanged(TorrentHandle *const torrent);
void handleTorrentsPrioritiesChanged();
void handleTorrentNameChanged(TorrentHandle *const torrent);
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
@@ -607,13 +609,13 @@ namespace BitTorrent
void updateSeedingLimitTimer();
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
void saveTorrentResumeData(TorrentHandle *const torrent);
void handleAlert(libtorrent::alert *a);
void dispatchTorrentAlert(libtorrent::alert *a);
void handleAddTorrentAlert(libtorrent::add_torrent_alert *p);
void handleStateUpdateAlert(libtorrent::state_update_alert *p);
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p);
void handleMetadataReceivedAlert(const libtorrent::metadata_received_alert *p);
void handleTorrentPausedAlert(const libtorrent::torrent_paused_alert *p);
void handleFileErrorAlert(libtorrent::file_error_alert *p);
void handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p);
void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert *p);
@@ -633,6 +635,8 @@ namespace BitTorrent
void createTorrentHandle(const libtorrent::torrent_handle &nativeHandle);
void saveResumeData();
void saveTorrentsQueue();
void removeTorrentsQueue();
#if LIBTORRENT_VERSION_NUM < 10100
void dispatchAlerts(libtorrent::alert *alertPtr);
@@ -657,6 +661,7 @@ namespace BitTorrent
CachedSettingValue<bool> m_announceToAllTrackers;
CachedSettingValue<bool> m_announceToAllTiers;
CachedSettingValue<int> m_asyncIOThreads;
CachedSettingValue<int> m_checkingMemUsage;
CachedSettingValue<int> m_diskCacheSize;
CachedSettingValue<int> m_diskCacheTTL;
CachedSettingValue<bool> m_useOSCache;

View File

@@ -110,7 +110,7 @@ void TorrentCreatorThread::run()
QStringList fileNames;
QHash<QString, boost::int64_t> fileSizeMap;
for (const auto &dir : qAsConst(dirs)) {
for (const auto &dir : asConst(dirs)) {
QStringList tmpNames; // natural sort files within each dir
QDirIterator fileIter(dir, QDir::Files);
@@ -126,7 +126,7 @@ void TorrentCreatorThread::run()
fileNames += tmpNames;
}
for (const auto &fileName : qAsConst(fileNames))
for (const auto &fileName : asConst(fileNames))
fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
}
@@ -141,14 +141,14 @@ void TorrentCreatorThread::run()
#endif
// Add url seeds
foreach (QString seed, m_params.urlSeeds) {
for (QString seed : asConst(m_params.urlSeeds)) {
seed = seed.trimmed();
if (!seed.isEmpty())
newTorrent.add_url_seed(seed.toStdString());
}
int tier = 0;
foreach (const QString &tracker, m_params.trackers) {
for (const QString &tracker : asConst(m_params.trackers)) {
if (tracker.isEmpty())
++tier;
else

View File

@@ -55,6 +55,7 @@
#include <windows.h>
#endif
#include "base/global.h"
#include "base/logger.h"
#include "base/preferences.h"
#include "base/profile.h"
@@ -77,7 +78,7 @@ namespace
ListType setToEntryList(const QSet<QString> &input)
{
ListType entryList;
foreach (const QString &setValue, input)
for (const QString &setValue : input)
entryList.emplace_back(setValue.toStdString());
return entryList;
}
@@ -158,14 +159,14 @@ namespace
{
// new constructor is available
template<typename T, typename std::enable_if<std::is_constructible<T, libt::torrent_info, bool>::value, int>::type = 0>
T makeTorrentCreator(const libtorrent::torrent_info & ti)
T makeTorrentCreator(const libtorrent::torrent_info &ti)
{
return T(ti, true);
}
// new constructor isn't available
template<typename T, typename std::enable_if<!std::is_constructible<T, libt::torrent_info, bool>::value, int>::type = 0>
T makeTorrentCreator(const libtorrent::torrent_info & ti)
T makeTorrentCreator(const libtorrent::torrent_info &ti)
{
return T(ti);
}
@@ -187,10 +188,12 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
, m_ratioLimit(params.ratioLimit)
, m_seedingTimeLimit(params.seedingTimeLimit)
, m_tempPathDisabled(params.disableTempPath)
, m_fastresumeDataRejected(false)
, m_hasMissingFiles(false)
, m_hasRootFolder(params.hasRootFolder)
, m_needsToSetFirstLastPiecePriority(false)
, m_needsToStartForced(params.forced)
, m_pauseWhenReady(params.paused)
{
if (m_useAutoTMM)
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
@@ -216,18 +219,18 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
m_hasRootFolder = false;
}
// "started" means "all initialization has completed and torrent has started regular processing".
// When torrent added/restored in "paused" state it become "started" immediately after construction.
// When it is added/restored in "resumed" state, it become "started" after it is really resumed
// (i.e. after receiving "torrent resumed" alert).
if (params.paused) {
m_startupState = Started;
}
else if (!params.restored) {
// Resume torrent because it was added in "resumed" state
// but it's actually paused during initialization
m_startupState = Starting;
resume(params.forced);
if (!hasMetadata()) {
// There is nothing to prepare
if (!m_pauseWhenReady) {
// Resume torrent because it was added in "resumed" state
// but it's actually paused during initialization.
m_startupState = Starting;
resume_impl(m_needsToStartForced);
}
else {
m_startupState = Started;
m_pauseWhenReady = false;
}
}
}
@@ -374,10 +377,9 @@ QString TorrentHandle::nativeActualSavePath() const
QList<TrackerEntry> TorrentHandle::trackers() const
{
QList<TrackerEntry> entries;
std::vector<libt::announce_entry> announces;
const std::vector<libt::announce_entry> announces = m_nativeHandle.trackers();
announces = m_nativeHandle.trackers();
foreach (const libt::announce_entry &tracker, announces)
for (const libt::announce_entry &tracker : announces)
entries << tracker;
return entries;
@@ -391,7 +393,7 @@ QHash<QString, TrackerInfo> TorrentHandle::trackerInfos() const
void TorrentHandle::addTrackers(const QList<TrackerEntry> &trackers)
{
QList<TrackerEntry> addedTrackers;
foreach (const TrackerEntry &tracker, trackers) {
for (const TrackerEntry &tracker : trackers) {
if (addTracker(tracker))
addedTrackers << tracker;
}
@@ -400,13 +402,13 @@ void TorrentHandle::addTrackers(const QList<TrackerEntry> &trackers)
m_session->handleTorrentTrackersAdded(this, addedTrackers);
}
void TorrentHandle::replaceTrackers(QList<TrackerEntry> trackers)
void TorrentHandle::replaceTrackers(const QList<TrackerEntry> &trackers)
{
QList<TrackerEntry> existingTrackers = this->trackers();
QList<TrackerEntry> addedTrackers;
std::vector<libt::announce_entry> announces;
foreach (const TrackerEntry &tracker, trackers) {
for (const TrackerEntry &tracker : trackers) {
announces.push_back(tracker.nativeEntry());
if (!existingTrackers.contains(tracker))
addedTrackers << tracker;
@@ -438,9 +440,9 @@ bool TorrentHandle::addTracker(const TrackerEntry &tracker)
QList<QUrl> TorrentHandle::urlSeeds() const
{
QList<QUrl> urlSeeds;
std::set<std::string> seeds = m_nativeHandle.url_seeds();
const std::set<std::string> seeds = m_nativeHandle.url_seeds();
foreach (const std::string &urlSeed, seeds)
for (const std::string &urlSeed : seeds)
urlSeeds.append(QUrl(urlSeed.c_str()));
return urlSeeds;
@@ -449,7 +451,7 @@ QList<QUrl> TorrentHandle::urlSeeds() const
void TorrentHandle::addUrlSeeds(const QList<QUrl> &urlSeeds)
{
QList<QUrl> addedUrlSeeds;
foreach (const QUrl &urlSeed, urlSeeds) {
for (const QUrl &urlSeed : urlSeeds) {
if (addUrlSeed(urlSeed))
addedUrlSeeds << urlSeed;
}
@@ -461,7 +463,7 @@ void TorrentHandle::addUrlSeeds(const QList<QUrl> &urlSeeds)
void TorrentHandle::removeUrlSeeds(const QList<QUrl> &urlSeeds)
{
QList<QUrl> removedUrlSeeds;
foreach (const QUrl &urlSeed, urlSeeds) {
for (const QUrl &urlSeed : urlSeeds) {
if (removeUrlSeed(urlSeed))
removedUrlSeeds << urlSeed;
}
@@ -506,7 +508,8 @@ bool TorrentHandle::needSaveResumeData() const
void TorrentHandle::saveResumeData()
{
m_nativeHandle.save_resume_data();
m_nativeHandle.save_resume_data(lt::torrent_handle::save_info_dict);
m_session->handleTorrentSaveResumeDataRequested(this);
}
int TorrentHandle::filesCount() const
@@ -596,8 +599,7 @@ bool TorrentHandle::removeTag(const QString &tag)
void TorrentHandle::removeAllTags()
{
// QT automatically copies the container in foreach, so it's safe to mutate it.
foreach (const QString &tag, m_tags)
for (const QString &tag : asConst(tags()))
removeTag(tag);
}
@@ -623,7 +625,7 @@ QString TorrentHandle::filePath(int index) const
QString TorrentHandle::fileName(int index) const
{
if (!hasMetadata()) return QString();
if (!hasMetadata()) return QString();
return Utils::Fs::fileName(filePath(index));
}
@@ -636,7 +638,7 @@ qlonglong TorrentHandle::fileSize(int index) const
// to all files in a torrent
QStringList TorrentHandle::absoluteFilePaths() const
{
if (!hasMetadata()) return QStringList();
if (!hasMetadata()) return QStringList();
QDir saveDir(savePath(true));
QStringList res;
@@ -647,7 +649,7 @@ QStringList TorrentHandle::absoluteFilePaths() const
QStringList TorrentHandle::absoluteFilePathsUnwanted() const
{
if (!hasMetadata()) return QStringList();
if (!hasMetadata()) return QStringList();
QDir saveDir(savePath(true));
QStringList res;
@@ -883,9 +885,9 @@ bool TorrentHandle::hasError() const
bool TorrentHandle::hasFilteredPieces() const
{
std::vector<int> pp = m_nativeHandle.piece_priorities();
const std::vector<int> pp = m_nativeHandle.piece_priorities();
foreach (const int priority, pp)
for (const int priority : pp)
if (priority == 0) return true;
return false;
@@ -973,12 +975,13 @@ qulonglong TorrentHandle::eta() const
QVector<qreal> TorrentHandle::filesProgress() const
{
std::vector<boost::int64_t> fp;
QVector<qreal> result;
m_nativeHandle.file_progress(fp, libt::torrent_handle::piece_granularity);
int count = static_cast<int>(fp.size());
const int count = static_cast<int>(fp.size());
QVector<qreal> result;
result.reserve(count);
for (int i = 0; i < count; ++i) {
qlonglong size = fileSize(i);
const qlonglong size = fileSize(i);
if ((size <= 0) || (fp[i] == size))
result << 1;
else
@@ -1086,7 +1089,7 @@ QList<PeerInfo> TorrentHandle::peers() const
m_nativeHandle.get_peer_info(nativePeers);
foreach (const libt::peer_info &peer, nativePeers)
for (const libt::peer_info &peer : nativePeers)
peers << PeerInfo(this, peer);
return peers;
@@ -1230,6 +1233,8 @@ bool TorrentHandle::setCategory(const QString &category)
void TorrentHandle::move(QString path)
{
if (m_startupState != Started) return;
m_useAutoTMM = false;
m_session->handleTorrentSavingModeChanged(this);
@@ -1268,6 +1273,7 @@ void TorrentHandle::forceDHTAnnounce()
void TorrentHandle::forceRecheck()
{
if (m_startupState != Started) return;
if (!hasMetadata()) return;
m_nativeHandle.force_recheck();
@@ -1275,7 +1281,8 @@ void TorrentHandle::forceRecheck()
if (isPaused()) {
m_nativeHandle.stop_when_ready(true);
resume_impl(true, true);
m_nativeHandle.auto_managed(true);
m_pauseWhenReady = true;
}
}
@@ -1285,6 +1292,8 @@ void TorrentHandle::setSequentialDownload(bool b)
m_nativeHandle.set_sequential_download(b);
m_nativeStatus.sequential_download = b; // prevent return cached value
}
saveResumeData();
}
void TorrentHandle::toggleSequentialDownload()
@@ -1331,6 +1340,8 @@ void TorrentHandle::setFirstLastPiecePriorityImpl(const bool enabled, const QVec
LogMsg(tr("Download first and last piece first: %1, torrent: '%2'")
.arg((enabled ? tr("On") : tr("Off")), name()));
saveResumeData();
}
void TorrentHandle::toggleFirstLastPiecePriority()
@@ -1340,6 +1351,13 @@ void TorrentHandle::toggleFirstLastPiecePriority()
void TorrentHandle::pause()
{
if (m_startupState != Started) return;
if (m_pauseWhenReady) return;
if (isChecking()) {
m_pauseWhenReady = true;
return;
}
if (isPaused()) return;
m_nativeHandle.auto_managed(false);
@@ -1354,10 +1372,13 @@ void TorrentHandle::pause()
void TorrentHandle::resume(bool forced)
{
resume_impl(forced, false);
if (m_startupState != Started) return;
m_pauseWhenReady = false;
resume_impl(forced);
}
void TorrentHandle::resume_impl(bool forced, bool uploadMode)
void TorrentHandle::resume_impl(bool forced)
{
if (hasError())
m_nativeHandle.clear_error();
@@ -1368,7 +1389,6 @@ void TorrentHandle::resume_impl(bool forced, bool uploadMode)
}
m_nativeHandle.auto_managed(!forced);
m_nativeHandle.set_upload_mode(uploadMode);
m_nativeHandle.resume();
}
@@ -1403,6 +1423,7 @@ void TorrentHandle::setTrackerLogin(const QString &username, const QString &pass
void TorrentHandle::renameFile(int index, const QString &name)
{
m_oldPath[LTFileIndex {index}].push_back(filePath(index));
++m_renameCount;
qDebug() << Q_FUNC_INFO << index << name;
m_nativeHandle.rename_file(index, Utils::Fs::toNativePath(name).toStdString());
@@ -1424,16 +1445,6 @@ bool TorrentHandle::saveTorrentFile(const QString &path)
return false;
}
void TorrentHandle::setFilePriority(int index, int priority)
{
std::vector<int> priorities = m_nativeHandle.file_priorities();
if ((priorities.size() > static_cast<quint64>(index)) && (priorities[index] != priority)) {
priorities[index] = priority;
prioritizeFiles(QVector<int>::fromStdVector(priorities));
}
}
void TorrentHandle::handleStateUpdate(const libt::torrent_status &nativeStatus)
{
updateStatus(nativeStatus);
@@ -1560,17 +1571,24 @@ void TorrentHandle::handleTorrentCheckedAlert(const libtorrent::torrent_checked_
Q_UNUSED(p);
qDebug("\"%s\" have just finished checking", qUtf8Printable(name()));
if (m_startupState == NotStarted) {
if (!m_hasMissingFiles) {
// Resume torrent because it was added in "resumed" state
// but it's actually paused during initialization.
m_startupState = Starting;
resume(m_needsToStartForced);
if (m_startupState == Preparing) {
if (!m_pauseWhenReady) {
if (!m_hasMissingFiles) {
// Resume torrent because it was added in "resumed" state
// but it's actually paused during initialization.
m_startupState = Starting;
resume_impl(m_needsToStartForced);
}
else {
// Torrent that has missing files is paused.
m_startupState = Started;
}
}
else {
// Torrent that has missing files is marked as "started"
// but it remains paused.
m_startupState = Started;
m_pauseWhenReady = false;
if (m_fastresumeDataRejected && !m_hasMissingFiles)
saveResumeData();
}
}
@@ -1594,10 +1612,10 @@ void TorrentHandle::handleTorrentFinishedAlert(const libtorrent::torrent_finishe
Q_UNUSED(p);
qDebug("Got a torrent finished alert for \"%s\"", qUtf8Printable(name()));
qDebug("Torrent has seed status: %s", m_hasSeedStatus ? "yes" : "no");
m_hasMissingFiles = false;
if (m_hasSeedStatus) return;
updateStatus();
m_hasMissingFiles = false;
m_hasSeedStatus = true;
adjustActualSavePath();
@@ -1621,9 +1639,14 @@ void TorrentHandle::handleTorrentPausedAlert(const libtorrent::torrent_paused_al
Q_UNUSED(p);
if (m_startupState == Started) {
updateStatus();
m_speedMonitor.reset();
m_session->handleTorrentPaused(this);
if (!m_pauseWhenReady) {
updateStatus();
m_speedMonitor.reset();
m_session->handleTorrentPaused(this);
}
else {
m_pauseWhenReady = false;
}
}
}
@@ -1645,8 +1668,8 @@ void TorrentHandle::handleSaveResumeDataAlert(const libtorrent::save_resume_data
libtorrent::entry &resumeData = useDummyResumeData ? dummyEntry : *(p->resume_data);
if (useDummyResumeData) {
resumeData["qBt-magnetUri"] = toMagnetUri().toStdString();
resumeData["qBt-paused"] = isPaused();
resumeData["qBt-forced"] = isForced();
resumeData["paused"] = isPaused();
resumeData["auto_managed"] = m_nativeStatus.auto_managed;
// Both firstLastPiecePriority and sequential need to be stored in the
// resume data if there is no metadata, otherwise they won't be
// restored if qBittorrent quits before the metadata are retrieved:
@@ -1668,6 +1691,14 @@ void TorrentHandle::handleSaveResumeDataAlert(const libtorrent::save_resume_data
resumeData["qBt-queuePosition"] = (nativeHandle().queue_position() + 1); // qBt starts queue at 1
resumeData["qBt-hasRootFolder"] = m_hasRootFolder;
if (m_pauseWhenReady) {
// We need to redefine these values when torrent starting/rechecking
// in "paused" state since native values can be logically wrong
// (torrent can be not paused and auto_managed when it is checking).
resumeData["paused"] = true;
resumeData["auto_managed"] = false;
}
m_session->handleTorrentResumeDataReady(this, resumeData);
}
@@ -1675,14 +1706,20 @@ void TorrentHandle::handleSaveResumeDataFailedAlert(const libtorrent::save_resum
{
// if torrent has no metadata we should save dummy fastresume data
// containing Magnet URI and qBittorrent own resume data only
if (p->error.value() == libt::errors::no_metadata)
if (p->error.value() == libt::errors::no_metadata) {
handleSaveResumeDataAlert(nullptr);
else
}
else {
LogMsg(tr("Save resume data failed. Torrent: \"%1\", error: \"%2\"")
.arg(name(), QString::fromLocal8Bit(p->error.message().c_str())), Log::CRITICAL);
m_session->handleTorrentResumeDataFailed(this);
}
}
void TorrentHandle::handleFastResumeRejectedAlert(const libtorrent::fastresume_rejected_alert *p)
{
m_fastresumeDataRejected = true;
if (p->error.value() == libt::errors::mismatching_file_size) {
// Mismatching file size (files were probably moved)
m_hasMissingFiles = true;
@@ -1690,51 +1727,72 @@ void TorrentHandle::handleFastResumeRejectedAlert(const libtorrent::fastresume_r
}
else {
LogMsg(tr("Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
.arg(name(), QString::fromStdString(p->message())), Log::CRITICAL);
.arg(name(), QString::fromStdString(p->message())), Log::WARNING);
}
}
void TorrentHandle::handleFileRenamedAlert(const libtorrent::file_renamed_alert *p)
{
#if LIBTORRENT_VERSION_NUM < 10100
QString newName = Utils::Fs::fromNativePath(QString::fromStdString(p->name));
#else
QString newName = Utils::Fs::fromNativePath(p->new_name());
#endif
// TODO: Check this!
if (filesCount() > 1) {
// Check if folders were renamed
QStringList oldPathParts = m_torrentInfo.origFilePath(p->index).split('/');
oldPathParts.removeLast();
QString oldPath = oldPathParts.join('/');
QStringList newPathParts = newName.split('/');
newPathParts.removeLast();
QString newPath = newPathParts.join('/');
if (!newPathParts.isEmpty() && (oldPath != newPath)) {
qDebug("oldPath(%s) != newPath(%s)", qUtf8Printable(oldPath), qUtf8Printable(newPath));
oldPath = QString("%1/%2").arg(savePath(true), oldPath);
qDebug("Detected folder renaming, attempt to delete old folder: %s", qUtf8Printable(oldPath));
QDir().rmpath(oldPath);
}
}
// We don't really need to call updateStatus() in this place.
// All we need to do is make sure we have a valid instance of the TorrentInfo object.
m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()};
// remove empty leftover folders
// for example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will
// be removed if they are empty
const QString oldFilePath = m_oldPath[LTFileIndex {p->index}].takeFirst();
const QString newFilePath = Utils::Fs::fromNativePath(p->new_name());
if (m_oldPath[LTFileIndex {p->index}].isEmpty())
m_oldPath.remove(LTFileIndex {p->index});
QVector<QStringRef> oldPathParts = oldFilePath.splitRef('/', QString::SkipEmptyParts);
oldPathParts.removeLast(); // drop file name part
QVector<QStringRef> newPathParts = newFilePath.splitRef('/', QString::SkipEmptyParts);
newPathParts.removeLast(); // drop file name part
#if defined(Q_OS_WIN)
const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
#else
const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
#endif
int pathIdx = 0;
while ((pathIdx < oldPathParts.size()) && (pathIdx < newPathParts.size())) {
if (oldPathParts[pathIdx].compare(newPathParts[pathIdx], caseSensitivity) != 0)
break;
++pathIdx;
}
for (int i = (oldPathParts.size() - 1); i >= pathIdx; --i) {
QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QLatin1String("/")));
oldPathParts.removeLast();
}
--m_renameCount;
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
m_moveFinishedTriggers.takeFirst()();
if (isPaused() && (m_renameCount == 0))
saveResumeData(); // otherwise the new path will not be saved
}
void TorrentHandle::handleFileRenameFailedAlert(const libtorrent::file_rename_failed_alert *p)
{
Q_UNUSED(p);
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
.arg(name(), filePath(p->index)
, QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
m_oldPath[LTFileIndex {p->index}].removeFirst();
if (m_oldPath[LTFileIndex {p->index}].isEmpty())
m_oldPath.remove(LTFileIndex {p->index});
--m_renameCount;
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
m_moveFinishedTriggers.takeFirst()();
if (isPaused() && (m_renameCount == 0))
saveResumeData(); // otherwise the new path will not be saved
}
void TorrentHandle::handleFileCompletedAlert(const libtorrent::file_completed_alert *p)
@@ -2062,7 +2120,7 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
if (created) {
// Hide the folder on Windows
qDebug() << "Hiding folder (Windows)";
std::wstring winPath = Utils::Fs::toNativePath(unwantedAbsPath).toStdWString();
std::wstring winPath = Utils::Fs::toNativePath(unwantedAbsPath).toStdWString();
DWORD dwAttrs = ::GetFileAttributesW(winPath.c_str());
bool ret = ::SetFileAttributesW(winPath.c_str(), dwAttrs | FILE_ATTRIBUTE_HIDDEN);
Q_ASSERT(ret != 0); Q_UNUSED(ret);
@@ -2100,7 +2158,7 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
QVector<qreal> TorrentHandle::availableFileFractions() const
{
const auto filesCount = this->filesCount();
const int filesCount = this->filesCount();
if (filesCount < 0) return {};
const QVector<int> piecesAvailability = pieceAvailability();
@@ -2109,12 +2167,13 @@ QVector<qreal> TorrentHandle::availableFileFractions() const
QVector<qreal> res;
res.reserve(filesCount);
TorrentInfo info = this->info();
for (int file = 0; file < filesCount; ++file) {
TorrentInfo::PieceRange filePieces = info.filePieces(file);
const TorrentInfo info = this->info();
for (int i = 0; i < filesCount; ++i) {
const TorrentInfo::PieceRange filePieces = info.filePieces(i);
int availablePieces = 0;
for (int piece = filePieces.first(); piece <= filePieces.last(); ++piece) {
availablePieces += piecesAvailability[piece] > 0 ? 1 : 0;
availablePieces += (piecesAvailability[piece] > 0) ? 1 : 0;
}
res.push_back(static_cast<qreal>(availablePieces) / filePieces.size());
}

View File

@@ -348,7 +348,6 @@ namespace BitTorrent
void renameFile(int index, const QString &name);
bool saveTorrentFile(const QString &path);
void prioritizeFiles(const QVector<int> &priorities);
void setFilePriority(int index, int priority);
void setRatioLimit(qreal limit);
void setSeedingTimeLimit(int limit);
void setUploadLimit(int limit);
@@ -356,7 +355,7 @@ namespace BitTorrent
void setSuperSeeding(bool enable);
void flushCache();
void addTrackers(const QList<TrackerEntry> &trackers);
void replaceTrackers(QList<TrackerEntry> trackers);
void replaceTrackers(const QList<TrackerEntry> &trackers);
void addUrlSeeds(const QList<QUrl> &urlSeeds);
void removeUrlSeeds(const QList<QUrl> &urlSeeds);
bool connectPeer(const PeerAddress &peerAddress);
@@ -386,6 +385,12 @@ namespace BitTorrent
private:
typedef boost::function<void ()> EventTrigger;
#if (LIBTORRENT_VERSION_NUM < 10200)
using LTFileIndex = int;
#else
using LTFileIndex = lt::file_index_t;
#endif
void updateStatus();
void updateStatus(const libtorrent::torrent_status &nativeStatus);
void updateState();
@@ -409,7 +414,7 @@ namespace BitTorrent
void handleMetadataReceivedAlert(const libtorrent::metadata_received_alert *p);
void handleStatsAlert(const libtorrent::stats_alert *p);
void resume_impl(bool forced, bool uploadMode);
void resume_impl(bool forced);
bool isMoveInProgress() const;
QString nativeActualSavePath() const;
@@ -447,6 +452,10 @@ namespace BitTorrent
QQueue<EventTrigger> m_moveFinishedTriggers;
int m_renameCount;
// Until libtorrent provide an "old_name" field in `file_renamed_alert`
// we will rely on this workaround to remove empty leftover folders
QHash<LTFileIndex, QVector<QString>> m_oldPath;
bool m_useAutoTMM;
// Persistent data
@@ -458,6 +467,7 @@ namespace BitTorrent
qreal m_ratioLimit;
int m_seedingTimeLimit;
bool m_tempPathDisabled;
bool m_fastresumeDataRejected;
bool m_hasMissingFiles;
bool m_hasRootFolder;
bool m_needsToSetFirstLastPiecePriority;
@@ -467,12 +477,15 @@ namespace BitTorrent
enum StartupState
{
NotStarted,
Starting,
Started
Preparing, // torrent is preparing to start regular processing
Starting, // torrent is prepared and starting to perform regular processing
Started // torrent is performing regular processing
};
StartupState m_startupState = Preparing;
// Handle torrent state when it starts performing some service job
// being in Paused state so it might be unpaused internally and then paused again
bool m_pauseWhenReady;
StartupState m_startupState = NotStarted;
bool m_unchecked = false;
};
}

View File

@@ -35,6 +35,7 @@
#include <QString>
#include <QUrl>
#include "base/global.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
@@ -105,10 +106,9 @@ TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexc
return TorrentInfo();
}
const qint64 fileSizeLimit = 100 * 1024 * 1024; // 100 MB
if (file.size() > fileSizeLimit) {
if (file.size() > MAX_TORRENT_SIZE) {
if (error)
*error = tr("File size exceeds max limit %1").arg(fileSizeLimit);
*error = tr("File size exceeds max limit %1").arg(Utils::Misc::friendlyUnit(MAX_TORRENT_SIZE));
return TorrentInfo();
}
@@ -247,7 +247,7 @@ QList<TrackerEntry> TorrentInfo::trackers() const
if (!isValid()) return QList<TrackerEntry>();
QList<TrackerEntry> trackers;
foreach (const libt::announce_entry &tracker, m_nativeInfo->trackers())
for (const libt::announce_entry &tracker : m_nativeInfo->trackers())
trackers.append(tracker);
return trackers;
@@ -258,7 +258,7 @@ QList<QUrl> TorrentInfo::urlSeeds() const
if (!isValid()) return QList<QUrl>();
QList<QUrl> urlSeeds;
foreach (const libt::web_seed_entry &webSeed, m_nativeInfo->web_seeds())
for (const libt::web_seed_entry &webSeed : m_nativeInfo->web_seeds())
if (webSeed.type == libt::web_seed_entry::url_seed)
urlSeeds.append(QUrl(webSeed.url.c_str()));
@@ -340,8 +340,13 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(int fileIndex) const
const libt::file_storage &files = nativeInfo()->files();
const auto fileSize = files.file_size(fileIndex);
const auto fileOffset = files.file_offset(fileIndex);
return makeInterval(static_cast<int>(fileOffset / pieceLength()),
static_cast<int>((fileOffset + fileSize - 1) / pieceLength()));
const int beginIdx = (fileOffset / pieceLength());
const int endIdx = ((fileOffset + fileSize - 1) / pieceLength());
if (fileSize <= 0)
return {beginIdx, 0};
return makeInterval(beginIdx, endIdx);
}
void TorrentInfo::renameFile(const int index, const QString &newPath)
@@ -350,7 +355,7 @@ void TorrentInfo::renameFile(const int index, const QString &newPath)
nativeInfo()->rename_file(index, Utils::Fs::toNativePath(newPath).toStdString());
}
int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
int BitTorrent::TorrentInfo::fileIndex(const QString &fileName) const
{
// the check whether the object is valid is not needed here
// because if filesCount() returns -1 the loop exits immediately

View File

@@ -29,16 +29,11 @@
#include "tracker.h"
#include <vector>
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#include "base/global.h"
#include "base/http/server.h"
#include "base/preferences.h"
#include "base/utils/bytearray.h"
#include "base/utils/string.h"
// static limits
static const int MAX_TORRENTS = 100;
@@ -60,7 +55,7 @@ bool Peer::operator==(const Peer &other) const
QString Peer::uid() const
{
return ip + ':' + QString::number(port);
return ip.toString() + ':' + QString::number(port);
}
libtorrent::entry Peer::toEntry(bool noPeerId) const
@@ -68,7 +63,7 @@ libtorrent::entry Peer::toEntry(bool noPeerId) const
libtorrent::entry::dictionary_type peerMap;
if (!noPeerId)
peerMap["id"] = libtorrent::entry(peerId.toStdString());
peerMap["ip"] = libtorrent::entry(ip.toStdString());
peerMap["ip"] = libtorrent::entry(ip.toString().toStdString());
peerMap["port"] = libtorrent::entry(port);
return libtorrent::entry(peerMap);
@@ -133,22 +128,13 @@ Http::Response Tracker::processRequest(const Http::Request &request, const Http:
void Tracker::respondToAnnounceRequest()
{
QMap<QString, QByteArray> queryParams;
// Parse GET parameters
using namespace Utils::ByteArray;
for (const QByteArray &param : copyAsConst(splitToViews(m_request.query, "&"))) {
const int sepPos = param.indexOf('=');
if (sepPos <= 0) continue; // ignores params without name
const QString paramName {QString::fromUtf8(param.constData(), sepPos)};
const QByteArray paramValue {param.mid(sepPos + 1)};
queryParams[paramName] = paramValue;
}
TrackerAnnounceRequest annonceReq;
const QMap<QString, QByteArray> &queryParams = m_request.query;
TrackerAnnounceRequest announceReq;
// IP
annonceReq.peer.ip = m_env.clientAddress.toString();
// Use the "ip" parameter provided from tracker request first, then fall back to client IP if invalid
const QHostAddress paramIP {QString::fromLatin1(queryParams.value("ip"))};
announceReq.peer.ip = paramIP.isNull() ? m_env.clientAddress : paramIP;
// 1. Get info_hash
if (!queryParams.contains("info_hash")) {
@@ -156,7 +142,7 @@ void Tracker::respondToAnnounceRequest()
status(101, "Missing info_hash");
return;
}
annonceReq.infoHash = queryParams.value("info_hash");
announceReq.infoHash = queryParams.value("info_hash");
// info_hash cannot be longer than 20 bytes
/*if (annonce_req.info_hash.toLatin1().length() > 20) {
qDebug("Tracker: Info_hash is not 20 byte long: %s (%d)", qUtf8Printable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length());
@@ -170,7 +156,7 @@ void Tracker::respondToAnnounceRequest()
status(102, "Missing peer_id");
return;
}
annonceReq.peer.peerId = queryParams.value("peer_id");
announceReq.peer.peerId = queryParams.value("peer_id");
// peer_id cannot be longer than 20 bytes
/*if (annonce_req.peer.peer_id.length() > 20) {
qDebug("Tracker: peer_id is not 20 byte long: %s", qUtf8Printable(annonce_req.peer.peer_id));
@@ -185,52 +171,52 @@ void Tracker::respondToAnnounceRequest()
return;
}
bool ok = false;
annonceReq.peer.port = queryParams.value("port").toInt(&ok);
if (!ok || (annonceReq.peer.port < 0) || (annonceReq.peer.port > 65535)) {
qDebug("Tracker: Invalid port number (%d)", annonceReq.peer.port);
announceReq.peer.port = queryParams.value("port").toInt(&ok);
if (!ok || (announceReq.peer.port < 0) || (announceReq.peer.port > 65535)) {
qDebug("Tracker: Invalid port number (%d)", announceReq.peer.port);
status(103, "Missing port");
return;
}
// 4. Get event
annonceReq.event = "";
announceReq.event = "";
if (queryParams.contains("event")) {
annonceReq.event = queryParams.value("event");
qDebug("Tracker: event is %s", qUtf8Printable(annonceReq.event));
announceReq.event = queryParams.value("event");
qDebug("Tracker: event is %s", qUtf8Printable(announceReq.event));
}
// 5. Get numwant
annonceReq.numwant = 50;
announceReq.numwant = 50;
if (queryParams.contains("numwant")) {
int tmp = queryParams.value("numwant").toInt();
if (tmp > 0) {
qDebug("Tracker: numwant = %d", tmp);
annonceReq.numwant = tmp;
announceReq.numwant = tmp;
}
}
// 6. no_peer_id (extension)
annonceReq.noPeerId = false;
announceReq.noPeerId = false;
if (queryParams.contains("no_peer_id"))
annonceReq.noPeerId = true;
announceReq.noPeerId = true;
// 7. TODO: support "compact" extension
// Done parsing, now let's reply
if (annonceReq.event == "stopped") {
unregisterPeer(annonceReq);
if (announceReq.event == "stopped") {
unregisterPeer(announceReq);
}
else {
registerPeer(annonceReq);
replyWithPeerList(annonceReq);
registerPeer(announceReq);
replyWithPeerList(announceReq);
}
}
void Tracker::registerPeer(const TrackerAnnounceRequest &annonceReq)
void Tracker::registerPeer(const TrackerAnnounceRequest &announceReq)
{
if (annonceReq.peer.port == 0) return;
if (announceReq.peer.port == 0) return;
if (!m_torrents.contains(annonceReq.infoHash)) {
if (!m_torrents.contains(announceReq.infoHash)) {
// Unknown torrent
if (m_torrents.size() == MAX_TORRENTS) {
// Reached max size, remove a random torrent
@@ -239,34 +225,34 @@ void Tracker::registerPeer(const TrackerAnnounceRequest &annonceReq)
}
// Register the user
PeerList &peers = m_torrents[annonceReq.infoHash];
if (!peers.contains(annonceReq.peer.uid())) {
PeerList &peers = m_torrents[announceReq.infoHash];
if (!peers.contains(announceReq.peer.uid())) {
// Unknown peer
if (peers.size() == MAX_PEERS_PER_TORRENT) {
// Too many peers, remove a random one
peers.erase(peers.begin());
}
}
peers[annonceReq.peer.uid()] = annonceReq.peer;
peers[announceReq.peer.uid()] = announceReq.peer;
}
void Tracker::unregisterPeer(const TrackerAnnounceRequest &annonceReq)
void Tracker::unregisterPeer(const TrackerAnnounceRequest &announceReq)
{
if (annonceReq.peer.port == 0) return;
if (announceReq.peer.port == 0) return;
if (m_torrents[annonceReq.infoHash].remove(annonceReq.peer.uid()) > 0)
if (m_torrents[announceReq.infoHash].remove(announceReq.peer.uid()) > 0)
qDebug("Tracker: Peer stopped downloading, deleting it from the list");
}
void Tracker::replyWithPeerList(const TrackerAnnounceRequest &annonceReq)
void Tracker::replyWithPeerList(const TrackerAnnounceRequest &announceReq)
{
// Prepare the entry for bencoding
libtorrent::entry::dictionary_type replyDict;
replyDict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL);
libtorrent::entry::list_type peerList;
for (const Peer &p : m_torrents.value(annonceReq.infoHash))
peerList.push_back(p.toEntry(annonceReq.noPeerId));
for (const Peer &p : m_torrents.value(announceReq.infoHash))
peerList.push_back(p.toEntry(announceReq.noPeerId));
replyDict["peers"] = libtorrent::entry(peerList);
const libtorrent::entry replyEntry(replyDict);

View File

@@ -32,10 +32,10 @@
#include <QHash>
#include <QObject>
#include <QHostAddress>
#include "base/http/irequesthandler.h"
#include "base/http/responsebuilder.h"
#include "base/http/types.h"
namespace libtorrent
{
@@ -51,7 +51,7 @@ namespace BitTorrent
{
struct Peer
{
QString ip;
QHostAddress ip;
QByteArray peerId;
int port;
@@ -90,9 +90,9 @@ namespace BitTorrent
private:
void respondToAnnounceRequest();
void registerPeer(const TrackerAnnounceRequest &annonceReq);
void unregisterPeer(const TrackerAnnounceRequest &annonceReq);
void replyWithPeerList(const TrackerAnnounceRequest &annonceReq);
void registerPeer(const TrackerAnnounceRequest &announceReq);
void unregisterPeer(const TrackerAnnounceRequest &announceReq);
void replyWithPeerList(const TrackerAnnounceRequest &announceReq);
Http::Server *m_server;
TorrentList m_torrents;

View File

@@ -44,10 +44,10 @@ namespace BitTorrent
public:
enum Status
{
NotContacted,
Working,
Updating,
NotWorking
NotContacted = 1,
Working = 2,
Updating = 3,
NotWorking = 4
};
TrackerEntry(const QString &url);

View File

@@ -30,11 +30,10 @@
RuntimeError::RuntimeError(const QString &message)
: std::runtime_error {message.toUtf8().data()}
, m_message {message}
{
}
QString RuntimeError::message() const
{
return m_message;
return what();
}

View File

@@ -36,7 +36,4 @@ class RuntimeError : public std::runtime_error
public:
explicit RuntimeError(const QString &message = "");
QString message() const;
private:
const QString m_message;
};

View File

@@ -64,7 +64,7 @@ FileSystemWatcher::FileSystemWatcher(QObject *parent)
QStringList FileSystemWatcher::directories() const
{
QStringList dirs = QFileSystemWatcher::directories();
for (const QDir &dir : qAsConst(m_watchedFolders))
for (const QDir &dir : asConst(m_watchedFolders))
dirs << dir.canonicalPath();
return dirs;
}
@@ -113,7 +113,7 @@ void FileSystemWatcher::scanLocalFolder(const QString &path)
void FileSystemWatcher::scanNetworkFolders()
{
for (const QDir &dir : qAsConst(m_watchedFolders))
for (const QDir &dir : asConst(m_watchedFolders))
processTorrentsInDir(dir);
}
@@ -162,7 +162,7 @@ void FileSystemWatcher::processTorrentsInDir(const QDir &dir)
const QStringList files = dir.entryList({"*.torrent", "*.magnet"}, QDir::Files);
for (const QString &file : files) {
const QString fileAbsPath = dir.absoluteFilePath(file);
if (file.endsWith(".magnet"))
if (file.endsWith(".magnet", Qt::CaseInsensitive))
torrents << fileAbsPath;
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid())
torrents << fileAbsPath;

View File

@@ -32,17 +32,15 @@
#include <QtGlobal>
const char C_TORRENT_FILE_EXTENSION[] = ".torrent";
const int MAX_TORRENT_SIZE = 100 * 1024 * 1024; // 100 MiB
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
template <typename T>
constexpr typename std::add_const<T>::type &qAsConst(T &t) noexcept { return t; }
constexpr typename std::add_const<T>::type &asConst(T &t) noexcept { return t; }
// prevent rvalue arguments:
// Forward rvalue as const
template <typename T>
void qAsConst(const T &&) = delete;
#endif
constexpr typename std::add_const<T>::type asConst(T &&t) noexcept { return std::move(t); }
// returns a const object copy
// Prevent const rvalue arguments
template <typename T>
constexpr typename std::add_const<T>::type copyAsConst(T &&t) noexcept { return std::move(t); }
void asConst(const T &&) = delete;

View File

@@ -66,7 +66,7 @@ void Connection::read()
case RequestParser::ParseStatus::Incomplete: {
const long bufferLimit = RequestParser::MAX_CONTENT_SIZE * 1.1; // some margin for headers
if (m_receivedData.size() > bufferLimit) {
Logger::instance()->addMessage(tr("Http request size exceeds limiation, closing socket. Limit: %ld, IP: %s")
Logger::instance()->addMessage(tr("Http request size exceeds limiation, closing socket. Limit: %1, IP: %2")
.arg(bufferLimit).arg(m_socket->peerAddress().toString()), Log::WARNING);
Response resp(413, "Payload Too Large");
@@ -79,7 +79,7 @@ void Connection::read()
return;
case RequestParser::ParseStatus::BadRequest: {
Logger::instance()->addMessage(tr("Bad Http request, closing socket. IP: %s")
Logger::instance()->addMessage(tr("Bad Http request, closing socket. IP: %1")
.arg(m_socket->peerAddress().toString()), Log::WARNING);
Response resp(400, "Bad Request");
@@ -133,7 +133,7 @@ bool Connection::acceptsGzipEncoding(QString codings)
const auto isCodingAvailable = [](const QStringList &list, const QString &encoding) -> bool
{
foreach (const QString &str, list) {
for (const QString &str : list) {
if (!str.startsWith(encoding))
continue;

View File

@@ -36,6 +36,7 @@
#include <QUrl>
#include <QUrlQuery>
#include "base/global.h"
#include "base/utils/bytearray.h"
#include "base/utils/string.h"
@@ -180,11 +181,29 @@ bool RequestParser::parseRequestLine(const QString &line)
m_request.method = match.captured(1);
// Request Target
const QByteArray decodedUrl {QByteArray::fromPercentEncoding(match.captured(2).toLatin1())};
const int sepPos = decodedUrl.indexOf('?');
m_request.path = QString::fromUtf8(decodedUrl.constData(), (sepPos == -1 ? decodedUrl.size() : sepPos));
if (sepPos >= 0)
m_request.query = decodedUrl.mid(sepPos + 1);
const QByteArray url {match.captured(2).toLatin1()};
const int sepPos = url.indexOf('?');
const QByteArray pathComponent = ((sepPos == -1) ? url : midView(url, 0, sepPos));
m_request.path = QString::fromUtf8(QByteArray::fromPercentEncoding(pathComponent));
if (sepPos >= 0) {
const QByteArray query = midView(url, (sepPos + 1));
// [rfc3986] 2.4 When to Encode or Decode
// URL components should be separated before percent-decoding
for (const QByteArray &param : asConst(splitToViews(query, "&"))) {
const int eqCharPos = param.indexOf('=');
if (eqCharPos <= 0) continue; // ignores params without name
const QByteArray nameComponent = midView(param, 0, eqCharPos);
const QByteArray valueComponent = midView(param, (eqCharPos + 1));
const QString paramName = QString::fromUtf8(QByteArray::fromPercentEncoding(nameComponent).replace('+', ' '));
const QByteArray paramValue = QByteArray::fromPercentEncoding(valueComponent).replace('+', ' ');
m_request.query[paramName] = paramValue;
}
}
// HTTP-version
m_request.version = match.captured(3);
@@ -200,7 +219,10 @@ bool RequestParser::parsePostMessage(const QByteArray &data)
// application/x-www-form-urlencoded
if (contentTypeLower.startsWith(CONTENT_TYPE_FORM_ENCODED)) {
QListIterator<QStringPair> i(QUrlQuery(data).queryItems(QUrl::FullyDecoded));
// [URL Standard] 5.1 application/x-www-form-urlencoded parsing
const QByteArray processedData = QByteArray(data).replace('+', ' ');
QListIterator<QStringPair> i(QUrlQuery(processedData).queryItems(QUrl::FullyDecoded));
while (i.hasNext()) {
const QStringPair pair = i.next();
m_request.posts[pair.first] = pair.second;

View File

@@ -97,16 +97,23 @@ void Server::incomingConnection(qintptr socketDescriptor)
#endif
Connection *c = new Connection(serverSocket, m_requestHandler, this);
m_connections.append(c);
m_connections.insert(c);
connect(serverSocket, &QAbstractSocket::disconnected, this, [c, this]() { removeConnection(c); });
}
void Server::removeConnection(Connection *connection)
{
m_connections.remove(connection);
connection->deleteLater();
}
void Server::dropTimedOutConnection()
{
QMutableListIterator<Connection *> i(m_connections);
QMutableSetIterator<Connection *> i(m_connections);
while (i.hasNext()) {
auto connection = i.next();
if (connection->isClosed() || connection->hasExpired(KEEP_ALIVE_DURATION)) {
delete connection;
Connection *connection = i.next();
if (connection->hasExpired(KEEP_ALIVE_DURATION)) {
connection->deleteLater();
i.remove();
}
}
@@ -146,9 +153,9 @@ QList<QSslCipher> Server::safeCipherList() const
const QStringList badCiphers = {"idea", "rc4"};
const QList<QSslCipher> allCiphers = QSslSocket::supportedCiphers();
QList<QSslCipher> safeCiphers;
foreach (const QSslCipher &cipher, allCiphers) {
for (const QSslCipher &cipher : allCiphers) {
bool isSafe = true;
foreach (const QString &badCipher, badCiphers) {
for (const QString &badCipher : badCiphers) {
if (cipher.name().contains(badCipher, Qt::CaseInsensitive)) {
isSafe = false;
break;

View File

@@ -31,6 +31,7 @@
#ifndef HTTP_SERVER_H
#define HTTP_SERVER_H
#include <QSet>
#include <QTcpServer>
#ifndef QT_NO_OPENSSL
@@ -63,9 +64,10 @@ namespace Http
private:
void incomingConnection(qintptr socketDescriptor);
void removeConnection(Connection *connection);
IRequestHandler *m_requestHandler;
QList<Connection *> m_connections; // for tracking persistent connections
QSet<Connection *> m_connections; // for tracking persistent connections
#ifndef QT_NO_OPENSSL
QList<QSslCipher> safeCipherList() const;

View File

@@ -52,6 +52,7 @@ namespace Http
const char HEADER_HOST[] = "host";
const char HEADER_ORIGIN[] = "origin";
const char HEADER_REFERER[] = "referer";
const char HEADER_REFERRER_POLICY[] = "referrer-policy";
const char HEADER_SET_COOKIE[] = "set-cookie";
const char HEADER_X_CONTENT_TYPE_OPTIONS[] = "x-content-type-options";
const char HEADER_X_FORWARDED_HOST[] = "x-forwarded-host";
@@ -96,8 +97,8 @@ namespace Http
QString version;
QString method;
QString path;
QByteArray query;
QStringMap headers;
QMap<QString, QByteArray> query;
QStringMap posts;
QVector<UploadedFile> files;
};
@@ -107,7 +108,7 @@ namespace Http
uint code;
QString text;
ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
ResponseStatus(uint code = 200, const QString &text = "OK"): code(code), text(text) {}
};
struct Response
@@ -116,7 +117,7 @@ namespace Http
QStringMap headers;
QByteArray content;
Response(uint code = 200, const QString& text = "OK"): status(code, text) {}
Response(uint code = 200, const QString &text = "OK"): status(code, text) {}
};
}

View File

@@ -39,6 +39,7 @@
#include <QSslError>
#include <QUrl>
#include "base/global.h"
#include "base/preferences.h"
#include "downloadhandler.h"
#include "proxyconfigurationmanager.h"
@@ -56,7 +57,7 @@ namespace
{
QDateTime now = QDateTime::currentDateTime();
QList<QNetworkCookie> cookies = Preferences::instance()->getNetworkCookies();
foreach (const QNetworkCookie &cookie, Preferences::instance()->getNetworkCookies()) {
for (const QNetworkCookie &cookie : asConst(Preferences::instance()->getNetworkCookies())) {
if (cookie.isSessionCookie() || (cookie.expirationDate() <= now))
cookies.removeAll(cookie);
}
@@ -68,7 +69,7 @@ namespace
{
QDateTime now = QDateTime::currentDateTime();
QList<QNetworkCookie> cookies = allCookies();
foreach (const QNetworkCookie &cookie, allCookies()) {
for (const QNetworkCookie &cookie : asConst(allCookies())) {
if (cookie.isSessionCookie() || (cookie.expirationDate() <= now))
cookies.removeAll(cookie);
}
@@ -83,7 +84,7 @@ namespace
{
QDateTime now = QDateTime::currentDateTime();
QList<QNetworkCookie> cookies = QNetworkCookieJar::cookiesForUrl(url);
foreach (const QNetworkCookie &cookie, QNetworkCookieJar::cookiesForUrl(url)) {
for (const QNetworkCookie &cookie : asConst(QNetworkCookieJar::cookiesForUrl(url))) {
if (!cookie.isSessionCookie() && (cookie.expirationDate() <= now))
cookies.removeAll(cookie);
}
@@ -95,7 +96,7 @@ namespace
{
QDateTime now = QDateTime::currentDateTime();
QList<QNetworkCookie> cookies = cookieList;
foreach (const QNetworkCookie &cookie, cookieList) {
for (const QNetworkCookie &cookie : cookieList) {
if (!cookie.isSessionCookie() && (cookie.expirationDate() <= now))
cookies.removeAll(cookie);
}
@@ -244,7 +245,7 @@ void Net::DownloadManager::applyProxySettings()
void Net::DownloadManager::handleReplyFinished(QNetworkReply *reply)
{
const ServiceID id = ServiceID::fromURL(reply->url());
const ServiceID id = ServiceID::fromURL(reply->request().url());
auto waitingJobsIter = m_waitingJobs.find(id);
if ((waitingJobsIter == m_waitingJobs.end()) || waitingJobsIter.value().isEmpty()) {
m_busyServices.remove(id);

View File

@@ -40,7 +40,7 @@ const QString KEY_PASSWORD = SETTINGS_KEY("Password");
namespace
{
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
inline bool isSameConfig(const Net::ProxyConfiguration &conf1, const Net::ProxyConfiguration &conf2)
{

View File

@@ -43,6 +43,7 @@
#include <QTcpSocket>
#endif
#include "base/global.h"
#include "base/logger.h"
#include "base/preferences.h"
@@ -291,7 +292,7 @@ QByteArray Smtp::encodeMimeHeader(const QString &key, const QString &value, QTex
if (!prefix.isEmpty()) line += prefix;
if (!value.contains("=?") && latin1->canEncode(value)) {
bool firstWord = true;
foreach (const QByteArray& word, value.toLatin1().split(' ')) {
for (const QByteArray &word : asConst(value.toLatin1().split(' '))) {
if (line.size() > 78) {
rv = rv + line + "\r\n";
line.clear();

View File

@@ -50,6 +50,7 @@
#include <CoreServices/CoreServices.h>
#endif
#include "global.h"
#include "logger.h"
#include "settingsstorage.h"
#include "utils/fs.h"
@@ -212,7 +213,7 @@ void Preferences::setCloseToTrayNotified(bool b)
{
setValue("Preferences/General/CloseToTrayNotified", b);
}
#endif
#endif // Q_OS_MAC
bool Preferences::isToolbarDisplayed() const
{
@@ -293,7 +294,7 @@ void Preferences::setWinStartup(bool b)
settings.remove("qBittorrent");
}
}
#endif
#endif // Q_OS_WIN
// Downloads
QString Preferences::lastLocationPath() const
@@ -505,7 +506,7 @@ void Preferences::setWebUiAuthSubnetWhitelistEnabled(bool enabled)
QList<Utils::Net::Subnet> Preferences::getWebUiAuthSubnetWhitelist() const
{
QList<Utils::Net::Subnet> subnets;
foreach (const QString &rawSubnet, value("Preferences/WebUI/AuthSubnetWhitelist").toStringList()) {
for (const QString &rawSubnet : asConst(value("Preferences/WebUI/AuthSubnetWhitelist").toStringList())) {
bool ok = false;
const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(rawSubnet.trimmed(), &ok);
if (ok)
@@ -626,6 +627,16 @@ void Preferences::setWebUiCSRFProtectionEnabled(bool enabled)
setValue("Preferences/WebUI/CSRFProtection", enabled);
}
bool Preferences::isWebUIHostHeaderValidationEnabled() const
{
return value("Preferences/WebUI/HostHeaderValidation", true).toBool();
}
void Preferences::setWebUIHostHeaderValidationEnabled(const bool enabled)
{
setValue("Preferences/WebUI/HostHeaderValidation", enabled);
}
bool Preferences::isWebUiHttpsEnabled() const
{
return value("Preferences/WebUI/HTTPS/Enabled", false).toBool();
@@ -957,7 +968,7 @@ void Preferences::setMagnetLinkAssoc(bool set)
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
}
#endif
#endif // Q_OS_WIN
#ifdef Q_OS_MAC
namespace
@@ -1013,7 +1024,7 @@ void Preferences::setMagnetLinkAssoc()
CFStringRef myBundleId = CFBundleGetIdentifier(CFBundleGetMainBundle());
LSSetDefaultHandlerForURLScheme(magnetUrlScheme, myBundleId);
}
#endif
#endif // Q_OS_MAC
int Preferences::getTrackerPort() const
{
@@ -1416,8 +1427,8 @@ void Preferences::setToolbarTextPosition(const int position)
QList<QNetworkCookie> Preferences::getNetworkCookies() const
{
QList<QNetworkCookie> cookies;
QStringList rawCookies = value("Network/Cookies").toStringList();
foreach (const QString &rawCookie, rawCookies)
const QStringList rawCookies = value("Network/Cookies").toStringList();
for (const QString &rawCookie : rawCookies)
cookies << QNetworkCookie::parseCookies(rawCookie.toUtf8());
return cookies;
@@ -1426,7 +1437,7 @@ QList<QNetworkCookie> Preferences::getNetworkCookies() const
void Preferences::setNetworkCookies(const QList<QNetworkCookie> &cookies)
{
QStringList rawCookies;
foreach (const QNetworkCookie &cookie, cookies)
for (const QNetworkCookie &cookie : cookies)
rawCookies << cookie.toRawForm();
setValue("Network/Cookies", rawCookies);
@@ -1467,10 +1478,10 @@ void Preferences::upgrade()
{
SettingsStorage *settingsStorage = SettingsStorage::instance();
QStringList labels = value("TransferListFilters/customLabels").toStringList();
const QStringList labels = value("TransferListFilters/customLabels").toStringList();
if (!labels.isEmpty()) {
QVariantMap categories = value("BitTorrent/Session/Categories").toMap();
foreach (const QString &label, labels) {
for (const QString &label : labels) {
if (!categories.contains(label))
categories[label] = "";
}

View File

@@ -201,6 +201,8 @@ public:
void setWebUiClickjackingProtectionEnabled(bool enabled);
bool isWebUiCSRFProtectionEnabled() const;
void setWebUiCSRFProtectionEnabled(bool enabled);
bool isWebUIHostHeaderValidationEnabled() const;
void setWebUIHostHeaderValidationEnabled(bool enabled);
// HTTPS
bool isWebUiHttpsEnabled() const;
@@ -297,7 +299,7 @@ public:
void setCloseToTrayNotified(bool b);
TrayIcon::Style trayIconStyle() const;
void setTrayIconStyle(TrayIcon::Style style);
#endif
#endif // Q_OS_MAC
// Stuff that don't appear in the Options GUI but are saved
// in the same file.

View File

@@ -435,13 +435,13 @@ namespace
if (leapSecond)
second = 59; // apparently a leap second - validate below, once time zone is known
int month = 0;
for ( ; (month < 12) && (parts[nmonth] != shortMonth[month]); ++month);
for ( ; (month < 12) && (parts[nmonth] != shortMonth[month]); ++month);
int dayOfWeek = -1;
if (!parts[nwday].isEmpty()) {
// Look up the weekday name
while (++dayOfWeek < 7 && (shortDay[dayOfWeek] != parts[nwday]));
while ((++dayOfWeek < 7) && (shortDay[dayOfWeek] != parts[nwday]));
if (dayOfWeek >= 7)
for (dayOfWeek = 0; dayOfWeek < 7 && (longDay[dayOfWeek] != parts[nwday]); ++dayOfWeek);
for (dayOfWeek = 0; (dayOfWeek < 7) && (longDay[dayOfWeek] != parts[nwday]); ++dayOfWeek);
}
// if (month >= 12 || dayOfWeek >= 7
@@ -450,7 +450,7 @@ namespace
int i = parts[nyear].size();
if (i < 4) {
// It's an obsolete year specification with less than 4 digits
year += (i == 2 && year < 50) ? 2000 : 1900;
year += ((i == 2) && (year < 50)) ? 2000 : 1900;
}
// Parse the UTC offset part
@@ -473,17 +473,17 @@ namespace
else {
// Check for an obsolete time zone name
QByteArray zone = parts[10].toLatin1();
if (zone.length() == 1 && isalpha(zone[0]) && toupper(zone[0]) != 'J') {
if ((zone.length() == 1) && (isalpha(zone[0])) && (toupper(zone[0]) != 'J')) {
negOffset = true; // military zone: RFC 2822 treats as '-0000'
}
else if (zone != "UT" && zone != "GMT") { // treated as '+0000'
else if ((zone != "UT") && (zone != "GMT")) { // treated as '+0000'
offset = (zone == "EDT")
? -4 * 3600
: ((zone == "EST") || (zone == "CDT"))
? -5 * 3600
: ((zone == "CST") || (zone == "MDT"))
? -6 * 3600
: (zone == "MST" || zone == "PDT")
: ((zone == "MST") || (zone == "PDT"))
? -7 * 3600
: (zone == "PST")
? -8 * 3600
@@ -502,12 +502,12 @@ namespace
}
}
QDate qdate(year, month + 1, day); // convert date, and check for out-of-range
if (!qdate.isValid())
QDate qDate(year, month + 1, day); // convert date, and check for out-of-range
if (!qDate.isValid())
return QDateTime::currentDateTime();
QTime qTime(hour, minute, second);
QDateTime result(qdate, qTime, Qt::UTC);
QDateTime result(qDate, qTime, Qt::UTC);
if (offset)
result = result.addSecs(-offset);
if (!result.isValid())
@@ -585,11 +585,13 @@ void Parser::parse_impl(const QByteArray &feedData)
emit finished(m_result);
m_result.articles.clear(); // clear articles only
m_articleIDs.clear();
}
void Parser::parseRssArticle(QXmlStreamReader &xml)
{
QVariantHash article;
QString altTorrentUrl;
while (!xml.atEnd()) {
xml.readNext();
@@ -605,6 +607,8 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
else if (name == QLatin1String("enclosure")) {
if (xml.attributes().value("type") == QLatin1String("application/x-bittorrent"))
article[Article::KeyTorrentURL] = xml.attributes().value(QLatin1String("url")).toString();
else if (xml.attributes().value("type").isEmpty())
altTorrentUrl = xml.attributes().value(QLatin1String("url")).toString();
}
else if (name == QLatin1String("link")) {
const QString text {xml.readElementText().trimmed()};
@@ -631,7 +635,10 @@ void Parser::parseRssArticle(QXmlStreamReader &xml)
}
}
m_result.articles.prepend(article);
if (article[Article::KeyTorrentURL].toString().isEmpty())
article[Article::KeyTorrentURL] = altTorrentUrl;
addArticle(article);
}
void Parser::parseRSSChannel(QXmlStreamReader &xml)
@@ -726,7 +733,7 @@ void Parser::parseAtomArticle(QXmlStreamReader &xml)
}
}
m_result.articles.prepend(article);
addArticle(article);
}
void Parser::parseAtomChannel(QXmlStreamReader &xml)
@@ -756,3 +763,34 @@ void Parser::parseAtomChannel(QXmlStreamReader &xml)
}
}
}
void Parser::addArticle(QVariantHash article)
{
QVariant &torrentURL = article[Article::KeyTorrentURL];
if (torrentURL.toString().isEmpty())
torrentURL = article[Article::KeyLink];
// If item does not have an ID, fall back to some other identifier.
QVariant &localId = article[Article::KeyId];
if (localId.toString().isEmpty())
localId = article.value(Article::KeyTorrentURL);
if (localId.toString().isEmpty())
localId = article.value(Article::KeyTitle);
if (localId.toString().isEmpty()) {
// The article could not be uniquely identified
// since it has no appropriate data.
// Just ignore it.
return;
}
if (m_articleIDs.contains(localId.toString())) {
// The article could not be uniquely identified
// since the Feed has duplicate identifiers.
// Just ignore it.
return;
}
m_articleIDs.insert(localId.toString());
m_result.articles.prepend(article);
}

View File

@@ -31,6 +31,7 @@
#include <QList>
#include <QObject>
#include <QSet>
#include <QString>
#include <QVariantHash>
@@ -65,9 +66,11 @@ namespace RSS
void parseRSSChannel(QXmlStreamReader &xml);
void parseAtomArticle(QXmlStreamReader &xml);
void parseAtomChannel(QXmlStreamReader &xml);
void addArticle(QVariantHash article);
QString m_baseUrl;
ParsingResult m_result;
QSet<QString> m_articleIDs;
};
}
}

View File

@@ -241,7 +241,7 @@ void AutoDownloader::importRules(const QByteArray &data, AutoDownloader::RulesFi
QByteArray AutoDownloader::exportRulesToJSONFormat() const
{
QJsonObject jsonObj;
for (const auto &rule : copyAsConst(rules()))
for (const auto &rule : asConst(rules()))
jsonObj.insert(rule.name(), rule.toJsonObject());
return QJsonDocument(jsonObj).toJson();
@@ -249,14 +249,14 @@ QByteArray AutoDownloader::exportRulesToJSONFormat() const
void AutoDownloader::importRulesFromJSONFormat(const QByteArray &data)
{
for (const auto &rule : copyAsConst(rulesFromJSON(data)))
for (const auto &rule : asConst(rulesFromJSON(data)))
insertRule(rule);
}
QByteArray AutoDownloader::exportRulesToLegacyFormat() const
{
QVariantHash dict;
for (const auto &rule : copyAsConst(rules()))
for (const auto &rule : asConst(rules()))
dict[rule.name()] = rule.toLegacyDict();
QByteArray data;
@@ -276,7 +276,7 @@ void AutoDownloader::importRulesFromLegacyFormat(const QByteArray &data)
if (in.status() != QDataStream::Ok)
throw ParsingError(tr("Invalid data format"));
for (const QVariant &val : qAsConst(dict))
for (const QVariant &val : asConst(dict))
insertRule(AutoDownloadRule::fromLegacyDict(val.toHash()));
}
@@ -373,7 +373,7 @@ void AutoDownloader::addJobForArticle(Article *article)
void AutoDownloader::processJob(const QSharedPointer<ProcessingJob> &job)
{
for (AutoDownloadRule &rule: m_rules) {
for (AutoDownloadRule &rule : m_rules) {
if (!rule.isEnabled()) continue;
if (!rule.feedURLs().contains(job->feedURL)) continue;
if (!rule.accepts(job->articleData)) continue;
@@ -435,8 +435,8 @@ void AutoDownloader::loadRules(const QByteArray &data)
void AutoDownloader::loadRulesLegacy()
{
SettingsPtr settings = Profile::instance().applicationSettings(QStringLiteral("qBittorrent-rss"));
QVariantHash rules = settings->value(QStringLiteral("download_rules")).toHash();
foreach (const QVariant &ruleVar, rules) {
const QVariantHash rules = settings->value(QStringLiteral("download_rules")).toHash();
for (const QVariant &ruleVar : rules) {
auto rule = AutoDownloadRule::fromLegacyDict(ruleVar.toHash());
if (!rule.name().isEmpty())
insertRule(rule);
@@ -451,7 +451,7 @@ void AutoDownloader::store()
m_savingTimer.stop();
QJsonObject jsonObj;
foreach (auto rule, m_rules)
for (const auto &rule : asConst(m_rules))
jsonObj.insert(rule.name(), rule.toJsonObject());
m_fileStorage->store(RulesFileName, QJsonDocument(jsonObj).toJson());
@@ -473,7 +473,7 @@ void AutoDownloader::resetProcessingQueue()
m_processingQueue.clear();
if (!m_processingEnabled) return;
foreach (Article *article, Session::instance()->rootFolder()->articles()) {
for (Article *article : asConst(Session::instance()->rootFolder()->articles())) {
if (!article->isRead() && !article->torrentUrl().isEmpty())
addJobForArticle(article);
}

View File

@@ -126,7 +126,7 @@ namespace RSS
bool smartFilter = false;
QStringList previouslyMatchedEpisodes;
mutable QString lastComputedEpisode;
mutable QStringList lastComputedEpisodes;
mutable QHash<QString, QRegularExpression> cachedRegexes;
bool operator==(const AutoDownloadRuleData &other) const
@@ -237,7 +237,7 @@ bool AutoDownloadRule::matchesMustContainExpression(const QString &articleTitle)
// Each expression is either a regex, or a set of wildcards separated by whitespace.
// Accept if any complete expression matches.
for (const QString &expression : qAsConst(m_dataPtr->mustContain)) {
for (const QString &expression : asConst(m_dataPtr->mustContain)) {
// A regex of the form "expr|" will always match, so do the same for wildcards
if (matchesExpression(articleTitle, expression))
return true;
@@ -246,14 +246,14 @@ bool AutoDownloadRule::matchesMustContainExpression(const QString &articleTitle)
return false;
}
bool AutoDownloadRule::matchesMustNotContainExpression(const QString& articleTitle) const
bool AutoDownloadRule::matchesMustNotContainExpression(const QString &articleTitle) const
{
if (m_dataPtr->mustNotContain.empty())
return true;
// Each expression is either a regex, or a set of wildcards separated by whitespace.
// Reject if any complete expression matches.
for (const QString &expression : qAsConst(m_dataPtr->mustNotContain)) {
for (const QString &expression : asConst(m_dataPtr->mustNotContain)) {
// A regex of the form "expr|" will always match, so do the same for wildcards
if (matchesExpression(articleTitle, expression))
return false;
@@ -262,10 +262,10 @@ bool AutoDownloadRule::matchesMustNotContainExpression(const QString& articleTit
return true;
}
bool AutoDownloadRule::matchesEpisodeFilterExpression(const QString& articleTitle) const
bool AutoDownloadRule::matchesEpisodeFilterExpression(const QString &articleTitle) const
{
// Reset the lastComputedEpisode, we don't want to leak it between matches
m_dataPtr->lastComputedEpisode.clear();
m_dataPtr->lastComputedEpisodes.clear();
if (m_dataPtr->episodeFilter.isEmpty())
return true;
@@ -332,7 +332,7 @@ bool AutoDownloadRule::matchesEpisodeFilterExpression(const QString& articleTitl
return false;
}
bool AutoDownloadRule::matchesSmartEpisodeFilter(const QString& articleTitle) const
bool AutoDownloadRule::matchesSmartEpisodeFilter(const QString &articleTitle) const
{
if (!useSmartFilter())
return true;
@@ -343,11 +343,35 @@ bool AutoDownloadRule::matchesSmartEpisodeFilter(const QString& articleTitle) co
// See if this episode has been downloaded before
const bool previouslyMatched = m_dataPtr->previouslyMatchedEpisodes.contains(episodeStr);
const bool isRepack = articleTitle.contains("REPACK", Qt::CaseInsensitive) || articleTitle.contains("PROPER", Qt::CaseInsensitive);
if (previouslyMatched && (!isRepack || !AutoDownloader::instance()->downloadRepacks()))
return false;
if (previouslyMatched) {
if (!AutoDownloader::instance()->downloadRepacks())
return false;
m_dataPtr->lastComputedEpisode = episodeStr;
// Now see if we've downloaded this particular repack/proper combination
const bool isRepack = articleTitle.contains("REPACK", Qt::CaseInsensitive);
const bool isProper = articleTitle.contains("PROPER", Qt::CaseInsensitive);
if (!isRepack && !isProper)
return false;
const QString fullEpisodeStr = QString("%1%2%3").arg(episodeStr,
isRepack ? "-REPACK" : "",
isProper ? "-PROPER" : "");
const bool previouslyMatchedFull = m_dataPtr->previouslyMatchedEpisodes.contains(fullEpisodeStr);
if (previouslyMatchedFull)
return false;
m_dataPtr->lastComputedEpisodes.append(fullEpisodeStr);
// If this is a REPACK and PROPER download, add the individual entries to the list
// so we don't download those
if (isRepack && isProper) {
m_dataPtr->lastComputedEpisodes.append(QString("%1-REPACK").arg(episodeStr));
m_dataPtr->lastComputedEpisodes.append(QString("%1-PROPER").arg(episodeStr));
}
}
m_dataPtr->lastComputedEpisodes.append(episodeStr);
return true;
}
@@ -379,10 +403,10 @@ bool AutoDownloadRule::accepts(const QVariantHash &articleData)
setLastMatch(articleData[Article::KeyDate].toDateTime());
if (!m_dataPtr->lastComputedEpisode.isEmpty()) {
// TODO: probably need to add a marker for PROPER/REPACK to avoid duplicate downloads
m_dataPtr->previouslyMatchedEpisodes.append(m_dataPtr->lastComputedEpisode);
m_dataPtr->lastComputedEpisode.clear();
// If there's a matched episode string, add that to the previously matched list
if (!m_dataPtr->lastComputedEpisodes.isEmpty()) {
m_dataPtr->previouslyMatchedEpisodes.append(m_dataPtr->lastComputedEpisodes);
m_dataPtr->lastComputedEpisodes.clear();
}
return true;
@@ -442,7 +466,7 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
QStringList feedURLs;
if (feedsVal.isString())
feedURLs << feedsVal.toString();
else foreach (const QJsonValue &urlVal, feedsVal.toArray())
else for (const QJsonValue &urlVal : asConst(feedsVal.toArray()))
feedURLs << urlVal.toString();
rule.setFeedURLs(feedURLs);
@@ -452,7 +476,7 @@ AutoDownloadRule AutoDownloadRule::fromJsonObject(const QJsonObject &jsonObj, co
previouslyMatched << previouslyMatchedVal.toString();
}
else {
foreach (const QJsonValue &val, previouslyMatchedVal.toArray())
for (const QJsonValue &val : asConst(previouslyMatchedVal.toArray()))
previouslyMatched << val.toString();
}
rule.setPreviouslyMatchedEpisodes(previouslyMatched);

View File

@@ -109,7 +109,7 @@ QList<Article *> Feed::articles() const
void Feed::markAsRead()
{
auto oldUnreadCount = m_unreadCount;
foreach (Article *article, m_articles) {
for (Article *article : asConst(m_articles)) {
if (!article->isRead()) {
article->disconnect(this);
article->markAsRead();
@@ -282,9 +282,9 @@ void Feed::loadArticles(const QByteArray &data)
return;
}
QJsonArray jsonArr = jsonDoc.array();
const QJsonArray jsonArr = jsonDoc.array();
int i = -1;
foreach (const QJsonValue &jsonVal, jsonArr) {
for (const QJsonValue &jsonVal : jsonArr) {
++i;
if (!jsonVal.isObject()) {
LogMsg(tr("Couldn't load RSS article '%1#%2'. Invalid data format.").arg(m_url).arg(i)
@@ -304,9 +304,9 @@ void Feed::loadArticles(const QByteArray &data)
void Feed::loadArticlesLegacy()
{
SettingsPtr qBTRSSFeeds = Profile::instance().applicationSettings(QStringLiteral("qBittorrent-rss-feeds"));
QVariantHash allOldItems = qBTRSSFeeds->value("old_items").toHash();
const QVariantHash allOldItems = qBTRSSFeeds->value("old_items").toHash();
foreach (const QVariant &var, allOldItems.value(m_url).toList()) {
for (const QVariant &var : asConst(allOldItems.value(m_url).toList())) {
auto hash = var.toHash();
// update legacy keys
hash[Article::KeyLink] = hash.take(QLatin1String("news_link"));
@@ -329,7 +329,7 @@ void Feed::store()
m_savingTimer.stop();
QJsonArray jsonArr;
foreach (Article *article, m_articles)
for (Article *article :asConst(m_articles))
jsonArr << article->toJsonObject();
m_session->dataFileStorage()->store(m_dataFileName, QJsonDocument(jsonArr).toJson());
@@ -419,24 +419,10 @@ int Feed::updateArticles(const QList<QVariantHash> &loadedArticles)
QVector<QVariantHash> newArticles;
newArticles.reserve(loadedArticles.size());
for (QVariantHash article : loadedArticles) {
QVariant &torrentURL = article[Article::KeyTorrentURL];
if (torrentURL.toString().isEmpty())
torrentURL = article[Article::KeyLink];
// If item does not have an ID, fall back to some other identifier.
QVariant &localId = article[Article::KeyId];
if (localId.toString().isEmpty())
localId = article.value(Article::KeyTorrentURL);
if (localId.toString().isEmpty())
localId = article.value(Article::KeyTitle);
if (localId.toString().isEmpty())
continue;
// If article has no publication date we use feed update time as a fallback.
// To prevent processing of "out-of-limit" articles we must not assign dates
// that are earlier than the dates of existing articles.
const Article *existingArticle = articleByGUID(localId.toString());
const Article *existingArticle = articleByGUID(article[Article::KeyId].toString());
if (existingArticle) {
dummyPubDate = existingArticle->date().addMSecs(-1);
continue;
@@ -474,7 +460,7 @@ int Feed::updateArticles(const QList<QVariantHash> &loadedArticles)
return (a1.first > a2.first);
});
if (sortData.size() > m_session->maxArticlesPerFeed())
if (sortData.size() > static_cast<uint>(m_session->maxArticlesPerFeed()))
sortData.resize(m_session->maxArticlesPerFeed());
int newArticlesCount = 0;
@@ -507,7 +493,7 @@ QJsonValue Feed::toJsonValue(bool withData) const
jsonObj.insert(KEY_HASERROR, hasError());
QJsonArray jsonArr;
for (Article *article : qAsConst(m_articles))
for (Article *article : asConst(m_articles))
jsonArr << article->toJsonObject();
jsonObj.insert(KEY_ARTICLES, jsonArr);
}

View File

@@ -47,7 +47,7 @@ Folder::~Folder()
{
emit aboutToBeDestroyed(this);
foreach (auto item, items())
for (auto item : asConst(items()))
delete item;
}
@@ -55,7 +55,7 @@ QList<Article *> Folder::articles() const
{
QList<Article *> news;
foreach (Item *item, items()) {
for (Item *item : asConst(items())) {
int n = news.size();
news << item->articles();
std::inplace_merge(news.begin(), news.begin() + n, news.end()
@@ -70,20 +70,20 @@ QList<Article *> Folder::articles() const
int Folder::unreadCount() const
{
int count = 0;
foreach (Item *item, items())
for (Item *item : asConst(items()))
count += item->unreadCount();
return count;
}
void Folder::markAsRead()
{
foreach (Item *item, items())
for (Item *item : asConst(items()))
item->markAsRead();
}
void Folder::refresh()
{
foreach (Item *item, items())
for (Item *item : asConst(items()))
item->refresh();
}
@@ -95,7 +95,7 @@ QList<Item *> Folder::items() const
QJsonValue Folder::toJsonValue(bool withData) const
{
QJsonObject jsonObj;
foreach (Item *item, items())
for (Item *item : asConst(items()))
jsonObj.insert(item->name(), item->toJsonValue(withData));
return jsonObj;
@@ -108,7 +108,7 @@ void Folder::handleItemUnreadCountChanged()
void Folder::cleanup()
{
foreach (Item *item, items())
for (Item *item : asConst(items()))
item->cleanup();
}
@@ -123,7 +123,7 @@ void Folder::addItem(Item *item)
connect(item, &Item::articleAboutToBeRemoved, this, &Item::articleAboutToBeRemoved);
connect(item, &Item::unreadCountChanged, this, &Folder::handleItemUnreadCountChanged);
for (auto article: copyAsConst(item->articles()))
for (auto article : asConst(item->articles()))
emit newArticle(article);
if (item->unreadCount() > 0)
@@ -134,7 +134,7 @@ void Folder::removeItem(Item *item)
{
Q_ASSERT(m_items.contains(item));
for (auto article: copyAsConst(item->articles()))
for (auto article : asConst(item->articles()))
emit articleAboutToBeRemoved(article);
item->disconnect(this);

View File

@@ -41,6 +41,7 @@
#include <QVariantHash>
#include "../asyncfilestorage.h"
#include "../global.h"
#include "../logger.h"
#include "../profile.h"
#include "../settingsstorage.h"
@@ -283,7 +284,7 @@ void Session::load()
void Session::loadFolder(const QJsonObject &jsonObj, Folder *folder)
{
bool updated = false;
foreach (const QString &key, jsonObj.keys()) {
for (const QString &key : asConst(jsonObj.keys())) {
const QJsonValue val {jsonObj[key]};
if (val.isString()) {
// previous format (reduced form) doesn't contain UID
@@ -355,7 +356,7 @@ void Session::loadLegacy()
const QString parentFolderPath = Item::parentPath(legacyPath);
const QString feedUrl = Item::relativeName(legacyPath);
foreach (const QString &folderPath, Item::expandPath(parentFolderPath))
for (const QString &folderPath : asConst(Item::expandPath(parentFolderPath)))
addFolder(folderPath);
const QString feedPath = feedAliases[i].isEmpty()

View File

@@ -35,6 +35,7 @@
#include "bittorrent/session.h"
#include "filesystemwatcher.h"
#include "global.h"
#include "preferences.h"
#include "utils/fs.h"
@@ -254,7 +255,7 @@ void ScanFoldersModel::addToFSWatcher(const QStringList &watchPaths)
if (!m_fsWatcher)
return; // addPath() wasn't called before this
foreach (const QString &path, watchPaths) {
for (const QString &path : watchPaths) {
QDir watchDir(path);
const QString canonicalWatchPath = watchDir.canonicalPath();
m_fsWatcher->addPath(canonicalWatchPath);
@@ -282,7 +283,7 @@ bool ScanFoldersModel::removePath(const QString &path, bool removeFromFSWatcher)
void ScanFoldersModel::removeFromFSWatcher(const QStringList &watchPaths)
{
foreach (const QString &path, watchPaths)
for (const QString &path : watchPaths)
m_fsWatcher->removePath(path);
}
@@ -326,7 +327,7 @@ void ScanFoldersModel::makePersistent()
{
QVariantHash dirs;
foreach (const PathData *pathData, m_pathList) {
for (const PathData *pathData : asConst(m_pathList)) {
if (pathData->downloadType == CUSTOM_LOCATION)
dirs.insert(Utils::Fs::fromNativePath(pathData->watchPath), Utils::Fs::fromNativePath(pathData->downloadPath));
else
@@ -350,7 +351,7 @@ void ScanFoldersModel::configure()
void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList)
{
foreach (const QString &file, pathList) {
for (const QString &file : pathList) {
qDebug("File %s added", qUtf8Printable(file));
BitTorrent::AddTorrentParams params;
@@ -359,7 +360,7 @@ void ScanFoldersModel::addTorrentsToSession(const QStringList &pathList)
else if (!downloadInDefaultFolder(file))
params.savePath = downloadPathTorrentFolder(file);
if (file.endsWith(".magnet")) {
if (file.endsWith(".magnet", Qt::CaseInsensitive)) {
QFile f(file);
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream str(&f);

View File

@@ -32,6 +32,7 @@
#include <QProcess>
#include <QTimer>
#include "../global.h"
#include "../utils/foreignapps.h"
#include "../utils/fs.h"
#include "searchpluginmanager.h"
@@ -138,7 +139,7 @@ void SearchHandler::readSearchOutput()
m_searchResultLineTruncated = lines.takeLast().trimmed();
QList<SearchResult> searchResultList;
foreach (const QByteArray &line, lines) {
for (const QByteArray &line : asConst(lines)) {
SearchResult searchResult;
if (parseSearchResult(QString::fromUtf8(line), searchResult))
searchResultList << searchResult;

View File

@@ -65,7 +65,7 @@ namespace
while (iter.hasNext())
dirs += iter.next();
for (const QString &dir : qAsConst(dirs)) {
for (const QString &dir : asConst(dirs)) {
// python 3: remove "__pycache__" folders
if (dir.endsWith("/__pycache__")) {
Utils::Fs::removeDirRecursive(dir);
@@ -101,9 +101,17 @@ SearchPluginManager::~SearchPluginManager()
SearchPluginManager *SearchPluginManager::instance()
{
if (!m_instance)
m_instance = new SearchPluginManager;
return m_instance;
}
void SearchPluginManager::freeInstance()
{
if (m_instance)
delete m_instance;
}
QStringList SearchPluginManager::allPlugins() const
{
return m_plugins.keys();
@@ -112,7 +120,7 @@ QStringList SearchPluginManager::allPlugins() const
QStringList SearchPluginManager::enabledPlugins() const
{
QStringList plugins;
for (const PluginInfo *plugin : qAsConst(m_plugins)) {
for (const PluginInfo *plugin : asConst(m_plugins)) {
if (plugin->enabled)
plugins << plugin->name;
}
@@ -123,9 +131,9 @@ QStringList SearchPluginManager::enabledPlugins() const
QStringList SearchPluginManager::supportedCategories() const
{
QStringList result;
for (const PluginInfo *plugin : qAsConst(m_plugins)) {
for (const PluginInfo *plugin : asConst(m_plugins)) {
if (plugin->enabled) {
foreach (QString cat, plugin->supportedCategories) {
for (const QString &cat : plugin->supportedCategories) {
if (!result.contains(cat))
result << cat;
}
@@ -146,7 +154,7 @@ QStringList SearchPluginManager::getPluginCategories(const QString &pluginName)
plugins << pluginName.trimmed();
QSet<QString> categories;
for (const QString &name : qAsConst(plugins)) {
for (const QString &name : asConst(plugins)) {
const PluginInfo *plugin = pluginInfo(name);
if (!plugin) continue; // plugin wasn't found
for (const QString &category : plugin->supportedCategories)
@@ -269,9 +277,8 @@ bool SearchPluginManager::uninstallPlugin(const QString &name)
QDir pluginsFolder(pluginsLocation());
QStringList filters;
filters << name + ".*";
QStringList files = pluginsFolder.entryList(filters, QDir::Files, QDir::Unsorted);
QString file;
foreach (file, files)
const QStringList files = pluginsFolder.entryList(filters, QDir::Files, QDir::Unsorted);
for (const QString &file : files)
Utils::Fs::forceRemove(pluginsFolder.absoluteFilePath(file));
// Remove it from supported engines
delete m_plugins.take(name);

View File

@@ -62,6 +62,7 @@ public:
~SearchPluginManager() override;
static SearchPluginManager *instance();
static void freeInstance();
QStringList allPlugins() const;
QStringList enabledPlugins() const;

View File

@@ -33,6 +33,7 @@
#include <QFile>
#include <QHash>
#include "global.h"
#include "logger.h"
#include "profile.h"
#include "utils/fs.h"
@@ -286,7 +287,7 @@ QString TransactionalSettings::deserialize(const QString &name, QVariantHash &da
// Copy everything into memory. This means even keys inserted in the file manually
// or that we don't touch directly in this code (eg disabled by ifdef). This ensures
// that they will be copied over when save our settings to disk.
foreach (const QString &key, settings->allKeys())
for (const QString &key : asConst(settings->allKeys()))
data.insert(key, settings->value(key));
return settings->fileName();

View File

@@ -38,8 +38,6 @@
// See issue #3059 for more details (https://github.com/qbittorrent/qBittorrent/issues/3059).
const char C_INFINITY[] = "";
const char C_NON_BREAKING_SPACE[] = " ";
const char C_UP[] = "";
const char C_DOWN[] = "";
const char C_COPYRIGHT[] = "©";
const char C_THIN_SPACE[] = "";
const char C_UTP[] = "μTP";

View File

@@ -229,7 +229,7 @@ namespace
return QString();
}
#endif
#endif // Q_OS_WIN
}
bool Utils::ForeignApps::PythonInfo::isValid() const

View File

@@ -133,7 +133,7 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const QString &path)
std::sort(dirList.begin(), dirList.end()
, [](const QString &l, const QString &r) { return l.count('/') > r.count('/'); });
for (const QString &p : qAsConst(dirList)) {
for (const QString &p : asConst(dirList)) {
// remove unwanted files
for (const QString &f : deleteFilesList) {
forceRemove(p + f);
@@ -141,7 +141,7 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const QString &path)
// remove temp files on linux (file ends with '~'), e.g. `filename~`
QDir dir(p);
QStringList tmpFileList = dir.entryList(QDir::Files);
const QStringList tmpFileList = dir.entryList(QDir::Files);
for (const QString &f : tmpFileList) {
if (f.endsWith('~'))
forceRemove(p + f);
@@ -236,7 +236,19 @@ bool Utils::Fs::isValidFileSystemName(const QString &name, bool allowSeparators)
{
if (name.isEmpty()) return false;
const QRegularExpression regex(allowSeparators ? "[:?\"*<>|]" : "[\\\\/:?\"*<>|]");
#if defined(Q_OS_WIN)
const QRegularExpression regex {allowSeparators
? QLatin1String("[:?\"*<>|]")
: QLatin1String("[\\\\/:?\"*<>|]")};
#elif defined(Q_OS_MACOS)
const QRegularExpression regex {allowSeparators
? QLatin1String("[\\0:]")
: QLatin1String("[\\0/:]")};
#else
const QRegularExpression regex {allowSeparators
? QLatin1String("[\\0]")
: QLatin1String("[\\0/]")};
#endif
return !name.contains(regex);
}
@@ -329,7 +341,7 @@ bool Utils::Fs::isNetworkFileSystem(const QString &path)
return ((strncmp(buf.f_fstypename, "cifs", sizeof(buf.f_fstypename)) == 0)
|| (strncmp(buf.f_fstypename, "nfs", sizeof(buf.f_fstypename)) == 0)
|| (strncmp(buf.f_fstypename, "smbfs", sizeof(buf.f_fstypename)) == 0));
#else
#else // Q_OS_WIN
QString file = path;
if (!file.endsWith('/'))
file += '/';
@@ -351,6 +363,6 @@ bool Utils::Fs::isNetworkFileSystem(const QString &path)
default:
return false;
}
#endif
#endif // Q_OS_WIN
}
#endif
#endif // Q_OS_HAIKU

View File

@@ -66,7 +66,7 @@
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
#include "base/utils/version.h"
#endif
#endif
#endif // DISABLE_GUI
#include "base/logger.h"
#include "base/unicodestrings.h"
@@ -391,7 +391,7 @@ QString Utils::Misc::getUserIDString()
QStringList Utils::Misc::toStringList(const QList<bool> &l)
{
QStringList ret;
foreach (const bool &b, l)
for (const bool b : l)
ret << (b ? "1" : "0");
return ret;
}
@@ -399,7 +399,7 @@ QStringList Utils::Misc::toStringList(const QList<bool> &l)
QList<int> Utils::Misc::intListfromStringList(const QStringList &l)
{
QList<int> ret;
foreach (const QString &s, l)
for (const QString &s : l)
ret << s.toInt();
return ret;
}
@@ -407,7 +407,7 @@ QList<int> Utils::Misc::intListfromStringList(const QStringList &l)
QList<bool> Utils::Misc::boolListfromStringList(const QStringList &l)
{
QList<bool> ret;
foreach (const QString &s, l)
for (const QString &s : l)
ret << (s == "1");
return ret;
}

View File

@@ -124,7 +124,7 @@ namespace Utils
return reinterpret_cast<T>(
::GetProcAddress(::LoadLibraryW(pathWchar.get()), funcName));
}
#endif
#endif // Q_OS_WIN
}
}

View File

@@ -97,18 +97,29 @@ namespace
}
else if (leftChar.isDigit() && rightChar.isDigit()) {
// Both are digits, compare the numbers
const auto consumeNumber = [](const QString &str, int &pos) -> int
const auto numberView = [](const QString &str, int &pos) -> QStringRef
{
const int start = pos;
while ((pos < str.size()) && str[pos].isDigit())
++pos;
return str.midRef(start, (pos - start)).toInt();
return str.midRef(start, (pos - start));
};
const int numL = consumeNumber(left, posL);
const int numR = consumeNumber(right, posR);
if (numL != numR)
return (numL - numR);
const QStringRef numViewL = numberView(left, posL);
const QStringRef numViewR = numberView(right, posR);
if (numViewL.length() != numViewR.length())
return (numViewL.length() - numViewR.length());
// both string/view has the same length
for (int i = 0; i < numViewL.length(); ++i) {
const QChar numL = numViewL.at(i);
const QChar numR = numViewR.at(i);
if (numL != numR)
return (numL.unicode() - numR.unicode());
}
// String + digits do match and we haven't hit the end of both strings
// then continue to consume the remainings
@@ -202,3 +213,14 @@ TriStateBool Utils::String::parseTriStateBool(const QString &string)
return TriStateBool::False;
return TriStateBool::Undefined;
}
QString Utils::String::join(const QVector<QStringRef> &strings, const QString &separator)
{
if (strings.empty())
return {};
QString ret = strings[0].toString();
for (int i = 1; i < strings.count(); ++i)
ret += (separator + strings[i]);
return ret;
}

View File

@@ -31,6 +31,7 @@
#define UTILS_STRING_H
#include <QString>
#include <QVector>
class QByteArray;
class QLatin1String;
@@ -60,7 +61,7 @@ namespace Utils
{
if (str.length() < 2) return str;
for (auto const quote : quotes) {
for (const auto &quote : quotes) {
if (str.startsWith(quote) && str.endsWith(quote))
return str.mid(1, str.length() - 2);
}
@@ -70,6 +71,8 @@ namespace Utils
bool parseBool(const QString &string, const bool defaultValue);
TriStateBool parseTriStateBool(const QString &string);
QString join(const QVector<QStringRef> &strings, const QString &separator);
}
}

View File

@@ -168,12 +168,12 @@ namespace Utils
{
if ((static_cast<std::size_t>(versionParts.size()) > N)
|| (static_cast<std::size_t>(versionParts.size()) < Mandatory))
throw std::runtime_error ("Incorrect number of version components");
throw std::runtime_error("Incorrect number of version components");
bool ok = false;
ComponentsArray res {{}};
for (std::size_t i = 0; i < static_cast<std::size_t>(versionParts.size()); ++i) {
res[i] = static_cast<T>(versionParts[i].toInt(&ok));
res[i] = static_cast<T>(versionParts[static_cast<typename StringsList::size_type>(i)].toInt(&ok));
if (!ok)
throw std::runtime_error("Can not parse version component");
}

View File

@@ -68,7 +68,7 @@ public:
"</table>"
"</p>")
.arg(tr("An advanced BitTorrent client programmed in C++, based on Qt toolkit and libtorrent-rasterbar.")
, tr("Copyright %1 2006-2018 The qBittorrent project").arg(QString::fromUtf8(C_COPYRIGHT))
, tr("Copyright %1 2006-2019 The qBittorrent project").arg(QString::fromUtf8(C_COPYRIGHT))
, tr("Home Page:")
, tr("Forum:")
, tr("Bug Tracker:"));

View File

@@ -30,23 +30,24 @@
#include <QDebug>
#include <QFile>
#include <QDir>
#include <QMenu>
#include <QPushButton>
#include <QShortcut>
#include <QString>
#include <QUrl>
#include <QVector>
#include "base/bittorrent/filepriority.h"
#include "base/bittorrent/magneturi.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/global.h"
#include "base/net/downloadhandler.h"
#include "base/net/downloadmanager.h"
#include "base/preferences.h"
#include "base/settingsstorage.h"
#include "base/torrentfileguard.h"
#include "base/unicodestrings.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
@@ -61,16 +62,14 @@
namespace
{
#define SETTINGS_KEY(name) QStringLiteral("AddNewTorrentDialog/" name)
const QString KEY_ENABLED = SETTINGS_KEY("Enabled");
const QString KEY_DEFAULTCATEGORY = SETTINGS_KEY("DefaultCategory");
const QString KEY_TREEHEADERSTATE = SETTINGS_KEY("TreeHeaderState");
const QString KEY_WIDTH = SETTINGS_KEY("Width");
const QString KEY_EXPANDED = SETTINGS_KEY("Expanded");
const QString KEY_TOPLEVEL = SETTINGS_KEY("TopLevel");
const QString KEY_SAVEPATHHISTORY = SETTINGS_KEY("SavePathHistory");
const QString KEY_SAVEPATHHISTORYLENGTH = SETTINGS_KEY("SavePathHistoryLength");
const QString KEY_REMEMBERLASTSAVEPATH = SETTINGS_KEY("RememberLastSavePath");
#define SETTINGS_KEY(name) "AddNewTorrentDialog/" name
const QString KEY_ENABLED = QStringLiteral(SETTINGS_KEY("Enabled"));
const QString KEY_DEFAULTCATEGORY = QStringLiteral(SETTINGS_KEY("DefaultCategory"));
const QString KEY_TREEHEADERSTATE = QStringLiteral(SETTINGS_KEY("TreeHeaderState"));
const QString KEY_TOPLEVEL = QStringLiteral(SETTINGS_KEY("TopLevel"));
const QString KEY_SAVEPATHHISTORY = QStringLiteral(SETTINGS_KEY("SavePathHistory"));
const QString KEY_SAVEPATHHISTORYLENGTH = QStringLiteral(SETTINGS_KEY("SavePathHistoryLength"));
const QString KEY_REMEMBERLASTSAVEPATH = QStringLiteral(SETTINGS_KEY("RememberLastSavePath"));
// just a shortcut
inline SettingsStorage *settings()
@@ -90,10 +89,13 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
, m_hasMetadata(false)
, m_oldIndex(0)
, m_torrentParams(inParams)
, m_storeDialogSize(SETTINGS_KEY("DialogSize"))
, m_storeSplitterState(SETTINGS_KEY("SplitterState"))
{
// TODO: set dialog file properties using m_torrentParams.filePriorities
m_ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
m_ui->lblMetaLoading->setVisible(false);
m_ui->progMetaLoading->setVisible(false);
@@ -143,18 +145,19 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
m_ui->categoryComboBox->addItem(defaultCategory);
m_ui->categoryComboBox->addItem("");
foreach (const QString &category, categories)
for (const QString &category : asConst(categories))
if (category != defaultCategory && category != m_torrentParams.category)
m_ui->categoryComboBox->addItem(category);
m_ui->contentTreeView->header()->setSortIndicator(0, Qt::AscendingOrder);
loadState();
// Signal / slots
connect(m_ui->toolButtonAdvanced, &QToolButton::clicked, this, &AddNewTorrentDialog::showAdvancedSettings);
connect(m_ui->doNotDeleteTorrentCheckBox, &QCheckBox::clicked, this, &AddNewTorrentDialog::doNotDeleteTorrentClicked);
QShortcut *editHotkey = new QShortcut(Qt::Key_F2, m_ui->contentTreeView, nullptr, nullptr, Qt::WidgetShortcut);
connect(editHotkey, &QShortcut::activated, this, &AddNewTorrentDialog::renameSelectedFile);
connect(m_ui->contentTreeView, &QAbstractItemView::doubleClicked, this, &AddNewTorrentDialog::renameSelectedFile);
connect(editHotkey, &QShortcut::activated
, this, [this]() { m_ui->contentTreeView->renameSelectedFile(m_torrentInfo); });
connect(m_ui->contentTreeView, &QAbstractItemView::doubleClicked
, this, [this]() { m_ui->contentTreeView->renameSelectedFile(m_torrentInfo); });
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus();
}
@@ -208,22 +211,17 @@ void AddNewTorrentDialog::setSavePathHistoryLength(int value)
void AddNewTorrentDialog::loadState()
{
Utils::Gui::resize(this, m_storeDialogSize);
m_ui->splitter->restoreState(m_storeSplitterState);
m_headerState = settings()->loadValue(KEY_TREEHEADERSTATE).toByteArray();
const QSize newSize = Utils::Gui::scaledSize(this, size());
const int width = settings()->loadValue(KEY_WIDTH, newSize.width()).toInt();
const int height = newSize.height();
resize(width, height);
m_ui->toolButtonAdvanced->setChecked(settings()->loadValue(KEY_EXPANDED).toBool());
}
void AddNewTorrentDialog::saveState()
{
m_storeDialogSize = size();
m_storeSplitterState = m_ui->splitter->saveState();
if (m_contentModel)
settings()->storeValue(KEY_TREEHEADERSTATE, m_ui->contentTreeView->header()->saveState());
settings()->storeValue(KEY_WIDTH, width());
settings()->storeValue(KEY_EXPANDED, m_ui->toolButtonAdvanced->isChecked());
}
void AddNewTorrentDialog::show(QString source, const BitTorrent::AddTorrentParams &inParams, QWidget *parent)
@@ -234,7 +232,7 @@ void AddNewTorrentDialog::show(QString source, const BitTorrent::AddTorrentParam
// Launch downloader
// TODO: Don't save loaded torrent to file, just use downloaded data!
Net::DownloadHandler *handler = Net::DownloadManager::instance()->download(
Net::DownloadRequest(source).limit(10485760 /* 10MB */).handleRedirectToMagnet(true).saveToFile(true));
Net::DownloadRequest(source).limit(MAX_TORRENT_SIZE).handleRedirectToMagnet(true).saveToFile(true));
connect(handler, static_cast<void (Net::DownloadHandler::*)(const QString &, const QString &)>(&Net::DownloadHandler::downloadFinished)
, dlg, &AddNewTorrentDialog::handleDownloadFinished);
connect(handler, &Net::DownloadHandler::downloadFailed, dlg, &AddNewTorrentDialog::handleDownloadFailed);
@@ -249,11 +247,7 @@ void AddNewTorrentDialog::show(QString source, const BitTorrent::AddTorrentParam
ok = dlg->loadTorrent(source);
if (ok)
#ifdef Q_OS_MAC
dlg->exec();
#else
dlg->open();
#endif
dlg->QDialog::show();
else
delete dlg;
}
@@ -313,7 +307,7 @@ bool AddNewTorrentDialog::loadTorrent(const QString &torrentPath)
return false;
}
m_ui->lblhash->setText(m_hash);
m_ui->labelHashData->setText(m_hash);
setupTreeview();
TMMChanged(m_ui->comboTTM->currentIndex());
return true;
@@ -358,7 +352,7 @@ bool AddNewTorrentDialog::loadMagnet(const BitTorrent::MagnetUri &magnetUri)
BitTorrent::Session::instance()->loadMetadata(magnetUri);
setMetadataProgressIndicator(true, tr("Retrieving metadata..."));
m_ui->lblhash->setText(m_hash);
m_ui->labelHashData->setText(m_hash);
return true;
}
@@ -372,33 +366,12 @@ void AddNewTorrentDialog::showEvent(QShowEvent *event)
raise();
}
void AddNewTorrentDialog::showAdvancedSettings(bool show)
{
const int minimumW = minimumWidth();
setMinimumWidth(width()); // to remain the same width
if (show) {
m_ui->toolButtonAdvanced->setText(QString::fromUtf8(C_UP));
m_ui->groupBoxSettings->setVisible(true);
m_ui->infoGroup->setVisible(true);
m_ui->contentTreeView->setVisible(m_hasMetadata);
static_cast<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(m_ui->checkBoxNeverShow) + 1, m_ui->toolButtonAdvanced);
}
else {
m_ui->toolButtonAdvanced->setText(QString::fromUtf8(C_DOWN));
m_ui->groupBoxSettings->setVisible(false);
m_ui->infoGroup->setVisible(false);
m_ui->buttonsHLayout->insertWidget(0, layout()->takeAt(layout()->indexOf(m_ui->checkBoxNeverShow) + 1)->widget());
}
adjustSize();
setMinimumWidth(minimumW);
}
void AddNewTorrentDialog::saveSavePathHistory() const
{
// Get current history
QStringList history = settings()->loadValue(KEY_SAVEPATHHISTORY).toStringList();
QVector<QDir> historyDirs;
for (const QString &path : qAsConst(history))
for (const QString &path : asConst(history))
historyDirs << QDir {path};
const QDir selectedSavePath {m_ui->savePath->selectedPath()};
@@ -446,7 +419,7 @@ void AddNewTorrentDialog::updateDiskSpaceLabel()
sizeString += tr("Free space on disk: %1").arg(Utils::Misc::friendlyUnit(Utils::Fs::freeDiskSpaceOnPath(
m_ui->savePath->selectedPath())));
sizeString += ')';
m_ui->labelSize->setText(sizeString);
m_ui->labelSizeData->setText(sizeString);
}
void AddNewTorrentDialog::onSavePathChanged(const QString &newPath)
@@ -479,107 +452,6 @@ void AddNewTorrentDialog::setSavePath(const QString &newPath)
onSavePathChanged(newPath);
}
void AddNewTorrentDialog::renameSelectedFile()
{
const QModelIndexList selectedIndexes = m_ui->contentTreeView->selectionModel()->selectedRows(0);
if (selectedIndexes.size() != 1) return;
const QModelIndex modelIndex = selectedIndexes.first();
if (!modelIndex.isValid()) return;
// Ask for new name
bool ok = false;
const bool isFile = (m_contentModel->itemType(modelIndex) == TorrentContentModelItem::FileType);
QString newName = AutoExpandableDialog::getText(this, tr("Renaming"), tr("New name:"), QLineEdit::Normal
, modelIndex.data().toString(), &ok, isFile).trimmed();
if (!ok) return;
if (newName.isEmpty() || !Utils::Fs::isValidFileSystemName(newName)) {
RaisedMessageBox::warning(this, tr("Rename error"),
tr("The name is empty or contains forbidden characters, please choose a different one."),
QMessageBox::Ok);
return;
}
if (isFile) {
const int fileIndex = m_contentModel->getFileIndex(modelIndex);
if (newName.endsWith(QB_EXT))
newName.chop(QB_EXT.size());
const QString oldFileName = m_torrentInfo.fileName(fileIndex);
const QString oldFilePath = m_torrentInfo.filePath(fileIndex);
const QString newFilePath = oldFilePath.leftRef(oldFilePath.size() - oldFileName.size()) + newName;
if (oldFileName == newName) {
qDebug("Name did not change: %s", qUtf8Printable(oldFileName));
return;
}
// check if that name is already used
for (int i = 0; i < m_torrentInfo.filesCount(); ++i) {
if (i == fileIndex) continue;
if (Utils::Fs::sameFileNames(m_torrentInfo.filePath(i), newFilePath)) {
RaisedMessageBox::warning(this, tr("Rename error"),
tr("This name is already in use in this folder. Please use a different name."),
QMessageBox::Ok);
return;
}
}
qDebug("Renaming %s to %s", qUtf8Printable(oldFilePath), qUtf8Printable(newFilePath));
m_torrentInfo.renameFile(fileIndex, newFilePath);
m_contentModel->setData(modelIndex, newName);
}
else {
// renaming a folder
QStringList pathItems;
pathItems << modelIndex.data().toString();
QModelIndex parent = m_contentModel->parent(modelIndex);
while (parent.isValid()) {
pathItems.prepend(parent.data().toString());
parent = m_contentModel->parent(parent);
}
const QString oldPath = pathItems.join('/');
pathItems.removeLast();
pathItems << newName;
QString newPath = pathItems.join('/');
if (Utils::Fs::sameFileNames(oldPath, newPath)) {
qDebug("Name did not change");
return;
}
if (!newPath.endsWith('/')) newPath += '/';
// Check for overwriting
for (int i = 0; i < m_torrentInfo.filesCount(); ++i) {
const QString currentName = m_torrentInfo.filePath(i);
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
if (currentName.startsWith(newPath, Qt::CaseSensitive)) {
#else
if (currentName.startsWith(newPath, Qt::CaseInsensitive)) {
#endif
RaisedMessageBox::warning(this, tr("The folder could not be renamed"),
tr("This name is already in use in this folder. Please use a different name."),
QMessageBox::Ok);
return;
}
}
// Replace path in all files
for (int i = 0; i < m_torrentInfo.filesCount(); ++i) {
const QString currentName = m_torrentInfo.filePath(i);
if (currentName.startsWith(oldPath)) {
QString newName = currentName;
newName.replace(0, oldPath.length(), newPath);
newName = Utils::Fs::expandPath(newName);
qDebug("Rename %s to %s", qUtf8Printable(currentName), qUtf8Printable(newName));
m_torrentInfo.renameFile(i, newName);
}
}
// Rename folder in torrent files model too
m_contentModel->setData(modelIndex, newName);
}
}
void AddNewTorrentDialog::populateSavePathComboBox()
{
m_ui->savePath->clear();
@@ -619,21 +491,21 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
QAction *act = myFilesLlistMenu.exec(QCursor::pos());
if (act) {
if (act == actRename) {
renameSelectedFile();
m_ui->contentTreeView->renameSelectedFile(m_torrentInfo);
}
else {
int prio = prio::NORMAL;
BitTorrent::FilePriority prio = BitTorrent::FilePriority::Normal;
if (act == m_ui->actionHigh)
prio = prio::HIGH;
prio = BitTorrent::FilePriority::High;
else if (act == m_ui->actionMaximum)
prio = prio::MAXIMUM;
prio = BitTorrent::FilePriority::Maximum;
else if (act == m_ui->actionNotDownloaded)
prio = prio::IGNORED;
prio = BitTorrent::FilePriority::Ignored;
qDebug("Setting files priority");
foreach (const QModelIndex &index, selectedRows) {
qDebug("Setting priority(%d) for file at row %d", prio, index.row());
m_contentModel->setData(m_contentModel->index(index.row(), PRIORITY, index.parent()), prio);
for (const QModelIndex &index : selectedRows) {
qDebug("Setting priority(%d) for file at row %d", static_cast<int>(prio), index.row());
m_contentModel->setData(m_contentModel->index(index.row(), PRIORITY, index.parent()), static_cast<int>(prio));
}
}
}
@@ -641,9 +513,6 @@ void AddNewTorrentDialog::displayContentTreeMenu(const QPoint &)
void AddNewTorrentDialog::accept()
{
if (!m_hasMetadata)
disconnect(this, SLOT(updateMetadata(const BitTorrent::TorrentInfo&)));
// TODO: Check if destination actually exists
m_torrentParams.skipChecking = m_ui->skipCheckingCheckBox->isChecked();
@@ -689,7 +558,6 @@ void AddNewTorrentDialog::accept()
void AddNewTorrentDialog::reject()
{
if (!m_hasMetadata) {
disconnect(this, SLOT(updateMetadata(BitTorrent::TorrentInfo)));
setMetadataProgressIndicator(false);
BitTorrent::Session::instance()->cancelLoadMetadata(m_hash);
}
@@ -701,7 +569,8 @@ void AddNewTorrentDialog::updateMetadata(const BitTorrent::TorrentInfo &info)
{
if (info.hash() != m_hash) return;
disconnect(this, SLOT(updateMetadata(BitTorrent::TorrentInfo)));
disconnect(BitTorrent::Session::instance(), &BitTorrent::Session::metadataLoaded, this, &AddNewTorrentDialog::updateMetadata);
if (!info.isValid()) {
RaisedMessageBox::critical(this, tr("I/O Error"), ("Invalid metadata."));
setMetadataProgressIndicator(false, tr("Invalid metadata"));
@@ -729,16 +598,16 @@ void AddNewTorrentDialog::setMetadataProgressIndicator(bool visibleIndicator, co
void AddNewTorrentDialog::setupTreeview()
{
if (!m_hasMetadata) {
setCommentText(tr("Not Available", "This comment is unavailable"));
m_ui->labelDate->setText(tr("Not Available", "This date is unavailable"));
m_ui->labelCommentData->setText(tr("Not Available", "This comment is unavailable"));
m_ui->labelDateData->setText(tr("Not Available", "This date is unavailable"));
}
else {
// Set dialog title
setWindowTitle(m_torrentInfo.name());
// Set torrent information
setCommentText(Utils::Misc::parseHtmlLinks(m_torrentInfo.comment()));
m_ui->labelDate->setText(!m_torrentInfo.creationDate().isNull() ? m_torrentInfo.creationDate().toString(Qt::DefaultLocaleShortDate) : tr("Not available"));
m_ui->labelCommentData->setText(Utils::Misc::parseHtmlLinks(m_torrentInfo.comment().toHtmlEscaped()));
m_ui->labelDateData->setText(!m_torrentInfo.creationDate().isNull() ? m_torrentInfo.creationDate().toString(Qt::DefaultLocaleShortDate) : tr("Not available"));
// Prepare content tree
m_contentModel = new TorrentContentFilterModel(this);
@@ -765,7 +634,6 @@ void AddNewTorrentDialog::setupTreeview()
}
updateDiskSpaceLabel();
showAdvancedSettings(settings()->loadValue(KEY_EXPANDED, false).toBool());
}
void AddNewTorrentDialog::handleDownloadFailed(const QString &url, const QString &reason)
@@ -800,7 +668,6 @@ void AddNewTorrentDialog::TMMChanged(int index)
m_ui->groupBoxSavePath->setEnabled(true);
m_ui->savePath->blockSignals(false);
m_ui->savePath->setCurrentIndex(m_oldIndex < m_ui->savePath->count() ? m_oldIndex : m_ui->savePath->count() - 1);
m_ui->toolButtonAdvanced->setEnabled(true);
}
else {
m_ui->groupBoxSavePath->setEnabled(false);
@@ -808,23 +675,9 @@ void AddNewTorrentDialog::TMMChanged(int index)
m_ui->savePath->clear();
QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->categoryComboBox->currentText());
m_ui->savePath->addItem(savePath);
m_ui->toolButtonAdvanced->setChecked(true);
m_ui->toolButtonAdvanced->setEnabled(false);
showAdvancedSettings(true);
}
}
void AddNewTorrentDialog::setCommentText(const QString &str) const
{
m_ui->commentLabel->setText(str);
// workaround for the additional space introduced by QScrollArea
int lineHeight = m_ui->commentLabel->fontMetrics().lineSpacing();
int lines = 1 + str.count('\n');
int height = lineHeight * lines;
m_ui->scrollArea->setMaximumHeight(height);
}
void AddNewTorrentDialog::doNotDeleteTorrentClicked(bool checked)
{
m_torrentGuard->setAutoRemove(!checked);

View File

@@ -31,11 +31,11 @@
#include <QDialog>
#include <QScopedPointer>
#include <QShortcut>
#include "base/bittorrent/addtorrentparams.h"
#include "base/bittorrent/infohash.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/settingvalue.h"
namespace BitTorrent
{
@@ -72,11 +72,9 @@ public:
static void show(QString source, QWidget *parent);
private slots:
void showAdvancedSettings(bool show);
void displayContentTreeMenu(const QPoint &);
void updateDiskSpaceLabel();
void onSavePathChanged(const QString &newPath);
void renameSelectedFile();
void updateMetadata(const BitTorrent::TorrentInfo &info);
void handleDownloadFailed(const QString &url, const QString &reason);
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
@@ -99,7 +97,6 @@ private:
void saveState();
void setMetadataProgressIndicator(bool visibleIndicator, const QString &labelText = QString());
void setupTreeview();
void setCommentText(const QString &str) const;
void setSavePath(const QString &newPath);
void showEvent(QShowEvent *event) override;
@@ -115,6 +112,9 @@ private:
int m_oldIndex;
QScopedPointer<TorrentFileGuard> m_torrentGuard;
BitTorrent::AddTorrentParams m_torrentParams;
CachedSettingValue<QSize> m_storeDialogSize;
CachedSettingValue<QByteArray> m_storeSplitterState;
};
#endif // ADDNEWTORRENTDIALOG_H

View File

@@ -6,37 +6,346 @@
<rect>
<x>0</x>
<y>0</y>
<width>414</width>
<height>630</height>
<width>900</width>
<height>620</height>
</rect>
</property>
<layout class="QVBoxLayout" name="AddNewTorrentDialogLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QFrame" name="torrentoptionsFrame">
<layout class="QVBoxLayout" name="mainlayout_addui">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="managementLayout">
<item>
<widget class="QLabel" name="labelTorrentManagementMode">
<property name="text">
<string>Torrent Management Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboTTM">
<property name="toolTip">
<string>Automatic mode means that various torrent properties(eg save path) will be decided by the associated category</string>
</property>
<item>
<property name="text">
<string>Manual</string>
</property>
</item>
<item>
<property name="text">
<string>Automatic</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBoxSavePath">
<property name="title">
<string>Save at</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="FileSystemPathComboEdit" name="savePath" native="true"/>
</item>
<item alignment="Qt::AlignRight">
<widget class="QCheckBox" name="checkBoxRememberLastSavePath">
<property name="text">
<string>Remember last used save path</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBoxSettings">
<property name="title">
<string>Torrent settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="categoryLayout">
<item>
<widget class="QLabel" name="labelCategory">
<property name="text">
<string>Category:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="categoryComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="insertPolicy">
<enum>QComboBox::InsertAtTop</enum>
</property>
</widget>
</item>
</layout>
</item>
<item alignment="Qt::AlignRight">
<widget class="QCheckBox" name="defaultCategoryCheckbox">
<property name="text">
<string>Set as default category</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="0">
<widget class="QCheckBox" name="doNotDeleteTorrentCheckBox">
<property name="toolTip">
<string>When checked, the .torrent file will not be deleted despite the settings at the &quot;Download&quot; page of the options dialog</string>
</property>
<property name="text">
<string>Do not delete .torrent file</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="firstLastCheckBox">
<property name="text">
<string>Download first and last pieces first</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="skipCheckingCheckBox">
<property name="text">
<string>Skip hash check</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="sequentialCheckBox">
<property name="text">
<string>Download in sequential order</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="createSubfolderCheckBox">
<property name="text">
<string>Create subfolder</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="startTorrentCheckBox">
<property name="text">
<string>Start torrent</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="infoGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Torrent information</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLabel" name="labelDate">
<property name="text">
<string>Date:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelSizeData"/>
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelDateData"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelSize">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="labelHashData">
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QScrollArea" name="scrollArea">
<property name="styleSheet">
<string notr="true">background-color: rgba(0, 0, 0, 0);</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>421</width>
<height>68</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labelCommentData">
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelHash">
<property name="text">
<string>Hash:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelComment">
<property name="text">
<string>Comment:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="TorrentContentTreeView" name="contentTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="buttonsHLayout">
<item>
<widget class="QLabel" name="labelTorrentManagementMode">
<widget class="QCheckBox" name="checkBoxNeverShow">
<property name="text">
<string>Torrent Management Mode:</string>
<string>Never show again</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboTTM">
<property name="toolTip">
<string>Automatic mode means that various torrent properties(eg save path) will be decided by the associated category</string>
</property>
<item>
<property name="text">
<string>Manual</string>
</property>
</item>
<item>
<property name="text">
<string>Automatic</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
@@ -53,289 +362,7 @@
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBoxSavePath">
<property name="title">
<string>Save at</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="FileSystemPathComboEdit" name="savePath" native="true"/>
</item>
<item>
<widget class="QCheckBox" name="checkBoxRememberLastSavePath">
<property name="text">
<string>Remember last used save path</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="doNotDeleteTorrentCheckBox">
<property name="toolTip">
<string>When checked, the .torrent file will not be deleted despite the settings at the &quot;Download&quot; page of the options dialog</string>
</property>
<property name="text">
<string>Do not delete .torrent file</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxNeverShow">
<property name="text">
<string>Never show again</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="toolButtonAdvanced">
<property name="text">
<string notr="true">▼</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBoxSettings">
<property name="title">
<string>Torrent settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="2">
<widget class="QCheckBox" name="defaultCategoryCheckbox">
<property name="text">
<string>Set as default category</string>
</property>
</widget>
</item>
<item row="0" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_1">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Category:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="categoryComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="insertPolicy">
<enum>QComboBox::InsertAtTop</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="startTorrentCheckBox">
<property name="text">
<string>Start torrent</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="skipCheckingCheckBox">
<property name="text">
<string>Skip hash check</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>35</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="createSubfolderCheckBox">
<property name="text">
<string>Create subfolder</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="sequentialCheckBox">
<property name="text">
<string>Download in sequential order</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="firstLastCheckBox">
<property name="text">
<string>Download first and last pieces first</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="infoGroup">
<property name="title">
<string>Torrent information</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="1">
<widget class="QLabel" name="lblhash">
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Hash:</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="TorrentContentTreeView" name="contentTreeView">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelDate">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Date:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelSize">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Comment:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>321</width>
<height>69</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="commentLabel">
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="buttonsHLayout">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QProgressBar" name="progMetaLoading">
<property name="sizePolicy">
@@ -353,13 +380,17 @@
<property name="textVisible">
<bool>false</bool>
</property>
<property name="format">
<string notr="true">%p%</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblMetaLoading"/>
<widget class="QLabel" name="lblMetaLoading">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
@@ -405,18 +436,6 @@
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>savePath</tabstop>
<tabstop>checkBoxRememberLastSavePath</tabstop>
<tabstop>checkBoxNeverShow</tabstop>
<tabstop>toolButtonAdvanced</tabstop>
<tabstop>startTorrentCheckBox</tabstop>
<tabstop>skipCheckingCheckBox</tabstop>
<tabstop>categoryComboBox</tabstop>
<tabstop>defaultCategoryCheckbox</tabstop>
<tabstop>scrollArea</tabstop>
<tabstop>contentTreeView</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
@@ -426,8 +445,8 @@
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>403</x>
<y>579</y>
<x>928</x>
<y>855</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
@@ -442,8 +461,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>403</x>
<y>579</y>
<x>928</x>
<y>855</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
@@ -451,22 +470,6 @@
</hint>
</hints>
</connection>
<connection>
<sender>categoryComboBox</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>AddNewTorrentDialog</receiver>
<slot>categoryChanged(int)</slot>
<hints>
<hint type="sourcelabel">
<x>337</x>
<y>205</y>
</hint>
<hint type="destinationlabel">
<x>403</x>
<y>160</y>
</hint>
</hints>
</connection>
<connection>
<sender>comboTTM</sender>
<signal>currentIndexChanged(int)</signal>
@@ -474,12 +477,28 @@
<slot>TMMChanged(int)</slot>
<hints>
<hint type="sourcelabel">
<x>200</x>
<y>19</y>
<x>250</x>
<y>53</y>
</hint>
<hint type="destinationlabel">
<x>206</x>
<y>294</y>
<x>467</x>
<y>249</y>
</hint>
</hints>
</connection>
<connection>
<sender>categoryComboBox</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>AddNewTorrentDialog</receiver>
<slot>categoryChanged(int)</slot>
<hints>
<hint type="sourcelabel">
<x>266</x>
<y>231</y>
</hint>
<hint type="destinationlabel">
<x>467</x>
<y>249</y>
</hint>
</hints>
</connection>

View File

@@ -36,6 +36,7 @@
#include <QNetworkInterface>
#include "base/bittorrent/session.h"
#include "base/global.h"
#include "base/preferences.h"
#include "base/unicodestrings.h"
#include "app/application.h"
@@ -83,6 +84,7 @@ enum AdvSettingsRows
#if LIBTORRENT_VERSION_NUM >= 10100
ASYNC_IO_THREADS,
#endif
CHECKING_MEM_USAGE,
// cache
DISK_CACHE,
DISK_CACHE_TTL,
@@ -152,6 +154,8 @@ void AdvancedSettings::saveAdvancedSettings()
// Async IO threads
session->setAsyncIOThreads(spinBoxAsyncIOThreads.value());
#endif
// Checking Memory Usage
session->setCheckingMemUsage(spinBoxCheckingMemUsage.value());
// Disk write cache
session->setDiskCacheSize(spinBoxCache.value());
session->setDiskCacheTTL(spinBoxCacheTTL.value());
@@ -286,13 +290,13 @@ void AdvancedSettings::updateInterfaceAddressCombo()
};
if (ifaceName.isEmpty()) {
foreach (const QHostAddress &ip, QNetworkInterface::allAddresses())
for (const QHostAddress &ip : asConst(QNetworkInterface::allAddresses()))
populateCombo(ip.toString(), ip.protocol());
}
else {
const QNetworkInterface iface = QNetworkInterface::interfaceFromName(ifaceName);
const QList<QNetworkAddressEntry> addresses = iface.addressEntries();
foreach (const QNetworkAddressEntry &entry, addresses) {
for (const QNetworkAddressEntry &entry : addresses) {
const QHostAddress ip = entry.ip();
populateCombo(ip.toString(), ip.protocol());
}
@@ -325,12 +329,19 @@ void AdvancedSettings::loadAdvancedSettings()
spinBoxAsyncIOThreads.setValue(session->asyncIOThreads());
addRow(ASYNC_IO_THREADS, tr("Asynchronous I/O threads"), &spinBoxAsyncIOThreads);
#endif
// Checking Memory Usage
spinBoxCheckingMemUsage.setMinimum(1);
spinBoxCheckingMemUsage.setValue(session->checkingMemUsage());
spinBoxCheckingMemUsage.setSuffix(tr(" MiB"));
addRow(CHECKING_MEM_USAGE, tr("Outstanding memory when checking torrents"), &spinBoxCheckingMemUsage);
// Disk write cache
spinBoxCache.setMinimum(-1);
// When build as 32bit binary, set the maximum at less than 2GB to prevent crashes.
// These macros may not be available on compilers other than MSVC and GCC
#if defined(__x86_64__) || defined(_M_X64)
spinBoxCache.setMaximum(4096);
spinBoxCache.setMaximum(33554431); // 32768GiB
#else
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
spinBoxCache.setMaximum(1536);
@@ -339,8 +350,8 @@ void AdvancedSettings::loadAdvancedSettings()
updateCacheSpinSuffix(spinBoxCache.value());
addRow(DISK_CACHE, tr("Disk cache"), &spinBoxCache);
// Disk cache expiry
spinBoxCacheTTL.setMinimum(15);
spinBoxCacheTTL.setMaximum(600);
spinBoxCacheTTL.setMinimum(1);
spinBoxCacheTTL.setMaximum(std::numeric_limits<int>::max());
spinBoxCacheTTL.setValue(session->diskCacheTTL());
spinBoxCacheTTL.setSuffix(tr(" s", " seconds"));
addRow(DISK_CACHE_TTL, tr("Disk cache expiry interval"), &spinBoxCacheTTL);
@@ -425,7 +436,7 @@ void AdvancedSettings::loadAdvancedSettings()
const QString currentInterface = session->networkInterface();
bool interfaceExists = currentInterface.isEmpty();
int i = 1;
foreach (const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) {
for (const QNetworkInterface &iface : asConst(QNetworkInterface::allInterfaces())) {
// This line fixes a Qt bug => https://bugreports.qt.io/browse/QTBUG-52633
// Tested in Qt 5.6.0. For more info see:
// https://github.com/qbittorrent/qBittorrent/issues/5131

View File

@@ -59,7 +59,7 @@ private:
template <typename T> void addRow(int row, const QString &rowText, T *widget);
QLabel labelQbtLink, labelLibtorrentLink;
QSpinBox spinBoxAsyncIOThreads, spinBoxCache, spinBoxSaveResumeDataInterval, spinBoxOutgoingPortsMin, spinBoxOutgoingPortsMax, spinBoxListRefresh, spinBoxMaxHalfOpen,
QSpinBox spinBoxAsyncIOThreads, spinBoxCheckingMemUsage, spinBoxCache, spinBoxSaveResumeDataInterval, spinBoxOutgoingPortsMin, spinBoxOutgoingPortsMax, spinBoxListRefresh, spinBoxMaxHalfOpen,
spinBoxTrackerPort, spinBoxCacheTTL, spinBoxSendBufferWatermark, spinBoxSendBufferLowWatermark,
spinBoxSendBufferWatermarkFactor, spinBoxSavePathHistoryLength;
QCheckBox checkBoxOsCache, checkBoxRecheckCompleted, checkBoxResolveCountries, checkBoxResolveHosts, checkBoxSuperSeeding,

View File

@@ -107,8 +107,8 @@ void BanListOptionsDialog::on_buttonBanIP_clicked()
void BanListOptionsDialog::on_buttonDeleteIP_clicked()
{
QModelIndexList selection = m_ui->bannedIPList->selectionModel()->selectedIndexes();
for (auto &i : selection)
const QModelIndexList selection = m_ui->bannedIPList->selectionModel()->selectedIndexes();
for (const auto &i : selection)
m_sortFilter->removeRow(i.row());
m_modified = true;

View File

@@ -33,6 +33,7 @@
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/global.h"
#include "guiiconprovider.h"
class CategoryModelItem
@@ -407,7 +408,7 @@ void CategoryFilterModel::populate()
const QString &category = i.key();
if (m_isSubcategoriesEnabled) {
CategoryModelItem *parent = m_rootItem;
foreach (const QString &subcat, session->expandCategory(category)) {
for (const QString &subcat : asConst(session->expandCategory(category))) {
const QString subcatName = shortName(subcat);
if (!parent->hasChild(subcatName)) {
new CategoryModelItem(
@@ -436,7 +437,7 @@ CategoryModelItem *CategoryFilterModel::findItem(const QString &fullName) const
return m_rootItem->child(fullName);
CategoryModelItem *item = m_rootItem;
foreach (const QString &subcat, BitTorrent::Session::expandCategory(fullName)) {
for (const QString &subcat : asConst(BitTorrent::Session::expandCategory(fullName))) {
const QString subcatName = shortName(subcat);
if (!item->hasChild(subcatName)) return nullptr;
item = item->child(subcatName);

View File

@@ -231,7 +231,7 @@ void CategoryFilterWidget::removeCategory()
void CategoryFilterWidget::removeUnusedCategories()
{
auto session = BitTorrent::Session::instance();
for (const QString &category : copyAsConst(session->categories().keys())) {
for (const QString &category : asConst(session->categories().keys())) {
if (model()->data(static_cast<CategoryFilterProxyModel *>(model())->index(category), Qt::UserRole) == 0)
session->removeCategory(category);
}

View File

@@ -30,6 +30,7 @@
#include <algorithm>
#include "base/global.h"
#include "base/net/downloadmanager.h"
#include "base/settingsstorage.h"
#include "cookiesmodel.h"
@@ -100,6 +101,6 @@ void CookiesDialog::onButtonDeleteClicked()
}
);
for (const QModelIndex &idx : idxs)
for (const QModelIndex &idx : asConst(idxs))
m_cookiesModel->removeRow(idx.row());
}

View File

@@ -32,6 +32,7 @@
#include <QDateTime>
#include <QPalette>
#include "base/global.h"
#include "guiiconprovider.h"
#include "loglistwidget.h"
#include "ui_executionlogwidget.h"
@@ -52,9 +53,9 @@ ExecutionLogWidget::ExecutionLogWidget(QWidget *parent, const Log::MsgTypes &typ
m_ui->tabBan->layout()->addWidget(m_peerList);
const Logger *const logger = Logger::instance();
foreach (const Log::Msg &msg, logger->getMessages())
for (const Log::Msg &msg : asConst(logger->getMessages()))
addLogMessage(msg);
foreach (const Log::Peer &peer, logger->getPeers())
for (const Log::Peer &peer : asConst(logger->getPeers()))
addPeerMessage(peer);
connect(logger, &Logger::newLogMessage, this, &ExecutionLogWidget::addLogMessage);
connect(logger, &Logger::newLogPeer, this, &ExecutionLogWidget::addPeerMessage);

View File

@@ -350,7 +350,7 @@ void FileSystemPathComboEdit::addItem(const QString &text)
editWidget<WidgetType>()->addItem(Utils::Fs::toNativePath(text));
}
void FileSystemPathComboEdit::insertItem(int index, const QString& text)
void FileSystemPathComboEdit::insertItem(int index, const QString &text)
{
editWidget<WidgetType>()->insertItem(index, Utils::Fs::toNativePath(text));
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Thomas Piccirello <thomas@piccirello.com>
* Copyright (C) 2017 Thomas Piccirello <thomas.piccirello@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -46,7 +46,7 @@ IPSubnetWhitelistOptionsDialog::IPSubnetWhitelistOptionsDialog(QWidget *parent)
m_ui->setupUi(this);
QStringList authSubnetWhitelistStringList;
for (const Utils::Net::Subnet &subnet : copyAsConst(Preferences::instance()->getWebUiAuthSubnetWhitelist()))
for (const Utils::Net::Subnet &subnet : asConst(Preferences::instance()->getWebUiAuthSubnetWhitelist()))
authSubnetWhitelistStringList << Utils::Net::subnetToString(subnet);
m_model = new QStringListModel(authSubnetWhitelistStringList, this);
@@ -99,7 +99,7 @@ void IPSubnetWhitelistOptionsDialog::on_buttonWhitelistIPSubnet_clicked()
void IPSubnetWhitelistOptionsDialog::on_buttonDeleteIPSubnet_clicked()
{
for (const auto &i : copyAsConst(m_ui->whitelistedIPSubnetList->selectionModel()->selectedIndexes()))
for (const auto &i : asConst(m_ui->whitelistedIPSubnetList->selectionModel()->selectedIndexes()))
m_sortFilter->removeRow(i.row());
m_modified = true;

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Thomas Piccirello <thomas@piccirello.com>
* Copyright (C) 2017 Thomas Piccirello <thomas.piccirello@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License

View File

@@ -36,6 +36,7 @@
#include <QListWidgetItem>
#include <QRegularExpression>
#include "base/global.h"
#include "guiiconprovider.h"
LogListWidget::LogListWidget(int maxLines, const Log::MsgTypes &types, QWidget *parent)
@@ -97,7 +98,7 @@ void LogListWidget::copySelection()
{
static const QRegularExpression htmlTag("<[^>]+>");
QStringList strings;
foreach (QListWidgetItem* it, selectedItems())
for (QListWidgetItem* it : asConst(selectedItems()))
strings << static_cast<QLabel*>(itemWidget(it))->text().remove(htmlTag);
QApplication::clipboard()->setText(strings.join('\n'));

View File

@@ -246,8 +246,7 @@ MainWindow::MainWindow(QWidget *parent)
m_transferListWidget = new TransferListWidget(hSplitter, this);
// transferList->setStyleSheet("QTreeView {border: none;}"); // borderless
m_propertiesWidget = new PropertiesWidget(hSplitter, this, m_transferListWidget);
m_transferListFiltersWidget = new TransferListFiltersWidget(m_splitter, m_transferListWidget);
m_transferListFiltersWidget->setDownloadTrackerFavicon(isDownloadTrackerFavicon());
m_transferListFiltersWidget = new TransferListFiltersWidget(m_splitter, m_transferListWidget, isDownloadTrackerFavicon());
hSplitter->addWidget(m_transferListWidget);
hSplitter->addWidget(m_propertiesWidget);
m_splitter->addWidget(m_transferListFiltersWidget);
@@ -284,7 +283,7 @@ MainWindow::MainWindow(QWidget *parent)
m_prioSeparatorMenu = m_ui->menuEdit->insertSeparator(m_ui->actionTopPriority);
#ifdef Q_OS_MAC
foreach (QAction *action, m_ui->toolBar->actions()) {
for (QAction *action : asConst(m_ui->toolBar->actions())) {
if (action->isSeparator()) {
QWidget *spacer = new QWidget(this);
spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
@@ -305,7 +304,7 @@ MainWindow::MainWindow(QWidget *parent)
spacer->setMinimumWidth(8);
m_ui->toolBar->addWidget(spacer);
}
#endif
#endif // Q_OS_MAC
// Transfer list slots
connect(m_ui->actionStart, &QAction::triggered, m_transferListWidget, &TransferListWidget::startSelectedTorrents);
@@ -526,7 +525,7 @@ void MainWindow::setTorrentAddedNotificationsEnabled(bool value)
bool MainWindow::isDownloadTrackerFavicon() const
{
return settings()->loadValue(KEY_DOWNLOAD_TRACKER_FAVICON, true).toBool();
return settings()->loadValue(KEY_DOWNLOAD_TRACKER_FAVICON, false).toBool();
}
void MainWindow::setDownloadTrackerFavicon(bool value)
@@ -1110,7 +1109,7 @@ void MainWindow::toggleVisibility(const QSystemTrayIcon::ActivationReason reason
break;
}
}
#endif
#endif // Q_OS_MAC
// Display About Dialog
void MainWindow::on_actionAbout_triggered()
@@ -1159,8 +1158,8 @@ void MainWindow::closeEvent(QCloseEvent *e)
#else
const bool goToSystrayOnExit = pref->closeToTray();
if (!m_forceExit && m_systrayIcon && goToSystrayOnExit && !this->isHidden()) {
hide();
e->accept();
e->ignore();
QTimer::singleShot(0, this, &QWidget::hide);
if (!pref->closeToTrayNotified()) {
showNotificationBaloon(tr("qBittorrent is closed to tray"), tr("This behavior can be changed in the settings. You won't be reminded again."));
pref->setCloseToTrayNotified(true);
@@ -1239,7 +1238,7 @@ bool MainWindow::event(QEvent *e)
qDebug() << "Has active window:" << (qApp->activeWindow() != nullptr);
// Check if there is a modal window
bool hasModalWindow = false;
foreach (QWidget *widget, QApplication::allWidgets()) {
for (QWidget *widget : asConst(QApplication::allWidgets())) {
if (widget->isModal()) {
hasModalWindow = true;
break;
@@ -1272,7 +1271,7 @@ bool MainWindow::event(QEvent *e)
default:
break;
}
#endif
#endif // Q_OS_MAC
return QMainWindow::event(e);
}
@@ -1285,7 +1284,7 @@ void MainWindow::dropEvent(QDropEvent *event)
// remove scheme
QStringList files;
if (event->mimeData()->hasUrls()) {
foreach (const QUrl &url, event->mimeData()->urls()) {
for (const QUrl &url : asConst(event->mimeData()->urls())) {
if (url.isEmpty())
continue;
@@ -1300,7 +1299,7 @@ void MainWindow::dropEvent(QDropEvent *event)
// differentiate ".torrent" files/links & magnet links from others
QStringList torrentFiles, otherFiles;
foreach (const QString &file, files) {
for (const QString &file : asConst(files)) {
const bool isTorrentLink = (file.startsWith("magnet:", Qt::CaseInsensitive)
|| file.endsWith(C_TORRENT_FILE_EXTENSION, Qt::CaseInsensitive)
|| Utils::Misc::isUrl(file));
@@ -1312,7 +1311,7 @@ void MainWindow::dropEvent(QDropEvent *event)
// Download torrents
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
foreach (const QString &file, torrentFiles) {
for (const QString &file : asConst(torrentFiles)) {
if (useTorrentAdditionDialog)
AddNewTorrentDialog::show(file, this);
else
@@ -1321,7 +1320,7 @@ void MainWindow::dropEvent(QDropEvent *event)
if (!torrentFiles.isEmpty()) return;
// Create torrent
foreach (const QString &file, otherFiles) {
for (const QString &file : asConst(otherFiles)) {
createTorrentTriggered(file);
// currently only hande the first entry
@@ -1333,7 +1332,7 @@ void MainWindow::dropEvent(QDropEvent *event)
// Decode if we accept drag 'n drop or not
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
foreach (const QString &mime, event->mimeData()->formats())
for (const QString &mime : asConst(event->mimeData()->formats()))
qDebug("mimeData: %s", mime.toLocal8Bit().data());
if (event->mimeData()->hasFormat("text/plain") || event->mimeData()->hasFormat("text/uri-list"))
event->acceptProposedAction();
@@ -1361,7 +1360,7 @@ void MainWindow::setupDockClickHandler()
MacUtils::overrideDockClickHandler(dockClickHandler);
}
#endif
#endif // Q_OS_MAC
/*****************************************************
* *
@@ -1382,7 +1381,7 @@ void MainWindow::on_actionOpen_triggered()
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
if (!pathsList.isEmpty()) {
foreach (QString file, pathsList) {
for (const QString &file : pathsList) {
qDebug("Dropped file %s on download list", qUtf8Printable(file));
if (useTorrentAdditionDialog)
AddNewTorrentDialog::show(file, this);
@@ -1635,7 +1634,7 @@ void MainWindow::showNotificationBaloon(QString title, QString msg) const
void MainWindow::downloadFromURLList(const QStringList &urlList)
{
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
foreach (QString url, urlList) {
for (QString url : urlList) {
if (((url.size() == 40) && !url.contains(QRegularExpression("[^0-9A-Fa-f]")))
|| ((url.size() == 32) && !url.contains(QRegularExpression("[^2-7A-Za-z]"))))
url = "magnet:?xt=urn:btih:" + url;

View File

@@ -71,6 +71,21 @@
#include "ui_optionsdialog.h"
#include "utils.h"
namespace
{
QStringList translatedWeekdayNames()
{
// return translated strings from Monday to Sunday in user selected locale
const QLocale locale {Preferences::instance()->getLocale()};
const QDate date {2018, 11, 5}; // Monday
QStringList ret;
for (int i = 0; i < 7; ++i)
ret.append(locale.toString(date.addDays(i), "dddd"));
return ret;
}
}
class WheelEventEater : public QObject
{
public:
@@ -145,10 +160,10 @@ OptionsDialog::OptionsDialog(QWidget *parent)
m_ui->hsplitter->setCollapsible(0, false);
m_ui->hsplitter->setCollapsible(1, false);
// Get apply button in button box
QList<QAbstractButton *> buttons = m_ui->buttonBox->buttons();
foreach (QAbstractButton *button, buttons) {
const QList<QAbstractButton *> buttons = m_ui->buttonBox->buttons();
for (QAbstractButton *button : buttons) {
if (m_ui->buttonBox->buttonRole(button) == QDialogButtonBox::ApplyRole) {
applyButton = button;
m_applyButton = button;
break;
}
}
@@ -164,8 +179,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
initializeLanguageCombo();
// Load week days (scheduler)
for (uint i = 1; i <= 7; ++i)
m_ui->comboBoxScheduleDays->addItem(QDate::longDayName(i, QDate::StandaloneFormat));
m_ui->comboBoxScheduleDays->addItems(translatedWeekdayNames());
// Load options
loadOptions();
@@ -290,7 +304,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
.arg(tr("Supported parameters (case sensitive):")
, tr("%N: Torrent name")
, tr("%L: Category")
, tr("%G: Tags (seperated by comma)")
, tr("%G: Tags (separated by comma)")
, tr("%F: Content path (same as root path for multifile torrent)")
, tr("%R: Root path (first torrent subdirectory path)")
, tr("%D: Save path")
@@ -390,6 +404,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, m_ui->IPSubnetWhitelistButton, &QPushButton::setEnabled);
connect(m_ui->checkClickjacking, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkCSRFProtection, &QCheckBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->groupHostHeaderValidation, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkDynDNS, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->comboDNSService, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
connect(m_ui->domainNameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
@@ -409,13 +424,13 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(m_ui->btnEditRules, &QPushButton::clicked, this, [this]() { AutomatedRssDownloader(this).exec(); });
// Disable apply Button
applyButton->setEnabled(false);
m_applyButton->setEnabled(false);
// Tab selection mechanism
connect(m_ui->tabSelection, &QListWidget::currentItemChanged, this, &ThisType::changePage);
// Load Advanced settings
advancedSettings = new AdvancedSettings(m_ui->tabAdvancedPage);
m_ui->advPageLayout->addWidget(advancedSettings);
connect(advancedSettings, &AdvancedSettings::settingsChanged, this, &ThisType::enableApplyButton);
m_advancedSettings = new AdvancedSettings(m_ui->tabAdvancedPage);
m_ui->advPageLayout->addWidget(m_advancedSettings);
connect(m_advancedSettings, &AdvancedSettings::settingsChanged, this, &ThisType::enableApplyButton);
m_ui->textFileLogPath->setDialogCaption(tr("Choose a save directory"));
m_ui->textFileLogPath->setMode(FileSystemPathEdit::Mode::DirectorySave);
@@ -438,9 +453,9 @@ OptionsDialog::OptionsDialog(QWidget *parent)
// disable mouse wheel event on widgets to avoid mis-selection
WheelEventEater *wheelEventEater = new WheelEventEater(this);
for (QComboBox *widget : copyAsConst(findChildren<QComboBox *>()))
for (QComboBox *widget : asConst(findChildren<QComboBox *>()))
widget->installEventFilter(wheelEventEater);
for (QSpinBox *widget : copyAsConst(findChildren<QSpinBox *>()))
for (QSpinBox *widget : asConst(findChildren<QSpinBox *>()))
widget->installEventFilter(wheelEventEater);
loadWindowState();
@@ -454,7 +469,7 @@ void OptionsDialog::initializeLanguageCombo()
// List language files
const QDir langDir(":/lang");
const QStringList langFiles = langDir.entryList(QStringList("qbittorrent_*.qm"), QDir::Files);
foreach (const QString langFile, langFiles) {
for (const QString &langFile : langFiles) {
QString localeStr = langFile.mid(12); // remove "qbittorrent_"
localeStr.chop(3); // Remove ".qm"
QString languageName;
@@ -478,7 +493,7 @@ OptionsDialog::~OptionsDialog()
saveWindowState();
foreach (const QString &path, addedScanDirs)
for (const QString &path : asConst(m_addedScanDirs))
ScanFoldersModel::instance()->removePath(path);
ScanFoldersModel::instance()->configure(); // reloads "removed" paths
delete m_ui;
@@ -526,7 +541,7 @@ void OptionsDialog::saveWindowState() const
void OptionsDialog::saveOptions()
{
applyButton->setEnabled(false);
m_applyButton->setEnabled(false);
Preferences *const pref = Preferences::instance();
// Load the translation
QString locale = getLocale();
@@ -550,8 +565,8 @@ void OptionsDialog::saveOptions()
pref->setTrayIconStyle(TrayIcon::Style(m_ui->comboTrayIcon->currentIndex()));
pref->setCloseToTray(closeToTray());
pref->setMinimizeToTray(minimizeToTray());
pref->setStartMinimized(startMinimized());
#endif
pref->setStartMinimized(startMinimized());
pref->setSplashScreenDisabled(isSplashScreenDisabled());
pref->setConfirmOnExit(m_ui->checkProgramExitConfirm->isChecked());
pref->setDontConfirmAutoExit(!m_ui->checkProgramAutoExitConfirm->isChecked());
@@ -610,11 +625,11 @@ void OptionsDialog::saveOptions()
AddNewTorrentDialog::setTopLevel(m_ui->checkAdditionDialogFront->isChecked());
session->setAddTorrentPaused(addTorrentsInPause());
session->setCreateTorrentSubfolder(m_ui->checkCreateSubfolder->isChecked());
ScanFoldersModel::instance()->removeFromFSWatcher(removedScanDirs);
ScanFoldersModel::instance()->addToFSWatcher(addedScanDirs);
ScanFoldersModel::instance()->removeFromFSWatcher(m_removedScanDirs);
ScanFoldersModel::instance()->addToFSWatcher(m_addedScanDirs);
ScanFoldersModel::instance()->makePersistent();
removedScanDirs.clear();
addedScanDirs.clear();
m_removedScanDirs.clear();
m_addedScanDirs.clear();
session->setTorrentExportDirectory(getTorrentExportDir());
session->setFinishedTorrentExportDirectory(getFinishedTorrentExportDir());
pref->setMailNotificationEnabled(m_ui->groupMailNotification->isChecked());
@@ -719,6 +734,7 @@ void OptionsDialog::saveOptions()
// Security
pref->setWebUiClickjackingProtectionEnabled(m_ui->checkClickjacking->isChecked());
pref->setWebUiCSRFProtectionEnabled(m_ui->checkCSRFProtection->isChecked());
pref->setWebUIHostHeaderValidationEnabled(m_ui->groupHostHeaderValidation->isChecked());
// DynDNS
pref->setDynDNSEnabled(m_ui->checkDynDNS->isChecked());
pref->setDynDNSService(m_ui->comboDNSService->currentIndex());
@@ -732,7 +748,7 @@ void OptionsDialog::saveOptions()
// End Web UI
// End preferences
// Save advanced settings
advancedSettings->saveAdvancedSettings();
m_advancedSettings->saveAdvancedSettings();
// Assume that user changed multiple settings
// so it's best to save immediately
pref->apply();
@@ -1082,6 +1098,7 @@ void OptionsDialog::loadOptions()
// Security
m_ui->checkClickjacking->setChecked(pref->isWebUiClickjackingProtectionEnabled());
m_ui->checkCSRFProtection->setChecked(pref->isWebUiCSRFProtectionEnabled());
m_ui->groupHostHeaderValidation->setChecked(pref->isWebUIHostHeaderValidationEnabled());
m_ui->checkDynDNS->setChecked(pref->isDynDNSEnabled());
m_ui->comboDNSService->setCurrentIndex(static_cast<int>(pref->getDynDNSService()));
@@ -1223,7 +1240,7 @@ int OptionsDialog::getMaxUploadsPerTorrent() const
void OptionsDialog::on_buttonBox_accepted()
{
if (applyButton->isEnabled()) {
if (m_applyButton->isEnabled()) {
if (!schedTimesOk()) {
m_ui->tabSelection->setCurrentRow(TAB_SPEED);
return;
@@ -1232,7 +1249,11 @@ void OptionsDialog::on_buttonBox_accepted()
m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
return;
}
applyButton->setEnabled(false);
if (!isAlternativeWebUIPathValid()) {
m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
return;
}
m_applyButton->setEnabled(false);
this->hide();
saveOptions();
}
@@ -1242,7 +1263,7 @@ void OptionsDialog::on_buttonBox_accepted()
void OptionsDialog::applySettings(QAbstractButton *button)
{
if (button == applyButton) {
if (button == m_applyButton) {
if (!schedTimesOk()) {
m_ui->tabSelection->setCurrentRow(TAB_SPEED);
return;
@@ -1251,6 +1272,10 @@ void OptionsDialog::applySettings(QAbstractButton *button)
m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
return;
}
if (!isAlternativeWebUIPathValid()) {
m_ui->tabSelection->setCurrentRow(TAB_WEBUI);
return;
}
saveOptions();
}
}
@@ -1274,7 +1299,7 @@ bool OptionsDialog::useAdditionDialog() const
void OptionsDialog::enableApplyButton()
{
applyButton->setEnabled(true);
m_applyButton->setEnabled(true);
}
void OptionsDialog::toggleComboRatioLimitAct()
@@ -1469,7 +1494,7 @@ void OptionsDialog::on_addScanFolderButton_clicked()
break;
default:
pref->setScanDirsLastPath(dir);
addedScanDirs << dir;
m_addedScanDirs << dir;
for (int i = 0; i < ScanFoldersModel::instance()->columnCount(); ++i)
m_ui->scanFoldersView->resizeColumnToContents(i);
enableApplyButton();
@@ -1487,9 +1512,9 @@ void OptionsDialog::on_removeScanFolderButton_clicked()
if (selected.isEmpty())
return;
Q_ASSERT(selected.count() == ScanFoldersModel::instance()->columnCount());
foreach (const QModelIndex &index, selected) {
for (const QModelIndex &index : selected) {
if (index.column() == ScanFoldersModel::WATCH)
removedScanDirs << index.data().toString();
m_removedScanDirs << index.data().toString();
}
ScanFoldersModel::instance()->removePath(selected.first().row(), false);
}
@@ -1733,6 +1758,15 @@ bool OptionsDialog::webUIAuthenticationOk()
return true;
}
bool OptionsDialog::isAlternativeWebUIPathValid()
{
if (m_ui->groupAltWebUI->isChecked() && m_ui->textWebUIRootFolder->selectedPath().trimmed().isEmpty()) {
QMessageBox::warning(this, tr("Location Error"), tr("The alternative Web UI files location cannot be blank."));
return false;
}
return true;
}
void OptionsDialog::on_banListButton_clicked()
{
// call dialog window

View File

@@ -172,15 +172,15 @@ private:
bool setSslCertificate(const QByteArray &cert);
bool schedTimesOk();
bool webUIAuthenticationOk();
bool isAlternativeWebUIPathValid();
QByteArray m_sslCert, m_sslKey;
Ui::OptionsDialog *m_ui;
QButtonGroup choiceLanguage;
QAbstractButton *applyButton;
AdvancedSettings *advancedSettings;
QList<QString> addedScanDirs;
QList<QString> removedScanDirs;
QAbstractButton *m_applyButton;
AdvancedSettings *m_advancedSettings;
QList<QString> m_addedScanDirs;
QList<QString> m_removedScanDirs;
};
#endif // OPTIONSDIALOG_H

View File

@@ -45,7 +45,7 @@
<enum>QListView::IconMode</enum>
</property>
<property name="currentRow">
<number>-1</number>
<number>0</number>
</property>
<item>
<property name="text">
@@ -941,7 +941,7 @@
<item row="3" column="0">
<widget class="QLabel" name="labelCategoryChanged">
<property name="text">
<string>When Category changed:</string>
<string>When Category Save Path changed:</string>
</property>
</widget>
</item>
@@ -1139,7 +1139,7 @@
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>To:</string>
<string comment="To receiver">To:</string>
</property>
</widget>
</item>
@@ -1159,7 +1159,7 @@
<item row="0" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>From:</string>
<string comment="From sender">From:</string>
</property>
</widget>
</item>
@@ -1915,7 +1915,7 @@
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string extracomment="from (time1 to time2)">From:</string>
<string comment="From start time">From:</string>
</property>
</widget>
</item>
@@ -1939,7 +1939,7 @@
<item row="0" column="2">
<widget class="QLabel" name="label_17">
<property name="text">
<string extracomment="time1 to time2">To:</string>
<string comment="To end time">To:</string>
</property>
</widget>
</item>
@@ -2944,28 +2944,6 @@ Specify an IPv4 or IPv6 address. You can specify &quot;0.0.0.0&quot; for any IPv
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLabel" name="labelServerDomains">
<property name="text">
<string>Server domains:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="textServerDomains">
<property name="toolTip">
<string>Whitelist for filtering HTTP Host header values.
In order to defend against DNS rebinding attack,
you should put in domain names used by WebUI server.
Use ';' to split multiple entries. Can use wildcard '*'.</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="checkWebUIUPnP">
<property name="text">
@@ -3177,17 +3155,60 @@ Use ';' to split multiple entries. Can use wildcard '*'.</string>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkClickjacking">
<property name="text">
<string>Enable clickjacking protection</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkCSRFProtection">
<property name="text">
<string>Enable Cross-Site Request Forgery (CSRF) protection</string>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Security</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_33">
<item>
<widget class="QCheckBox" name="checkClickjacking">
<property name="text">
<string>Enable clickjacking protection</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkCSRFProtection">
<property name="text">
<string>Enable Cross-Site Request Forgery (CSRF) protection</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupHostHeaderValidation">
<property name="title">
<string>Enable Host header validation</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_32">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLabel" name="labelServerDomains">
<property name="text">
<string>Server domains:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="textServerDomains">
<property name="toolTip">
<string>Whitelist for filtering HTTP Host header values.
In order to defend against DNS rebinding attack,
you should put in domain names used by WebUI server.
Use ';' to split multiple entries. Can use wildcard '*'.</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>

View File

@@ -36,7 +36,7 @@
#include <QStyleOptionProgressBar>
#include <QStyleOptionViewItem>
#ifdef Q_OS_WIN
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
#include <QProxyStyle>
#endif
@@ -76,7 +76,7 @@ public:
newopt.minimum = 0;
newopt.state |= QStyle::State_Enabled;
newopt.textVisible = true;
#ifndef Q_OS_WIN
#if !defined(Q_OS_WIN) && !defined(Q_OS_MACOS)
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
#else
// XXX: To avoid having the progress text on the right of the bar

View File

@@ -141,7 +141,7 @@ bool ProgramUpdater::isVersionMoreRecent(const QString &remoteVersion) const
qDebug() << Q_FUNC_INFO << "local version:" << localVersion << "/" << QBT_VERSION;
QStringList remoteParts = remoteVersion.split('.');
QStringList localParts = localVersion.split('.');
for (int i = 0; i<qMin(remoteParts.size(), localParts.size()); ++i) {
for (int i = 0; i < qMin(remoteParts.size(), localParts.size()); ++i) {
if (remoteParts[i].toInt() > localParts[i].toInt())
return true;
if (remoteParts[i].toInt() < localParts[i].toInt())

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