Compare commits

..

269 Commits

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

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

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

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

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

and rebregenerate ./configure
2017-11-27 19:16:03 +02:00
sledgehammer999
1399be50cb Fix crash on some systems when creating address object for 255.255.255.255
Closes #7735.
2017-11-27 19:14:41 +02:00
Vladimir Golovnev (Glassez)
52dcf32cc8 Implement Import/Export RSS rules in JSON format 2017-11-27 19:14:40 +02:00
Vladimir Golovnev (Glassez)
52b2b807ab Implement Import/Export RSS rules in legacy format 2017-11-27 19:14:38 +02:00
sledgehammer999
5cf4f00824 Remove examples from gpl.html. Closes #7749. 2017-11-27 19:14:37 +02:00
TheNicker
faa6fad025 Fixed blurry text under Windows by setting DPI awareness to default 2017-11-27 19:14:36 +02:00
Vladimir Golovnev (Glassez)
9f94bbce3a Fix RSS Parser
Closes #7751
Closes #7763
Closes #7768
Closes #7786
2017-11-27 19:14:35 +02:00
Chocobo1
5c49b2486c Change MixedModeAlgorithm default to TCP. Closes #7779.
MixedModeAlgorithm::Proportional will throttle TCP connections when utp
is in use and users is expecting maximum speed no matter what, so now
disable the throttling.
2017-11-27 19:14:34 +02:00
thoradia
4f6e7f97c6 Fix missing include in rss_feed.cpp
Fixes #7805.
2017-11-27 19:14:33 +02:00
sledgehammer999
7751c5b75c Merge pull request #7835 from heirecka/v4_0_x
Fix build with --disable-webui
2017-11-23 01:50:21 +02:00
Heiko Becker
a1a9f3317b Fix build with --disable-webui
"app/application.cpp:108:7: error: class 'Application' does not have
any field named 'm_webui'"
2017-11-22 21:30:08 +01:00
sledgehammer999
fb20f59a96 Bump to 4.0.1 2017-11-22 01:18:04 +02:00
sledgehammer999
a15e3407b0 Bump API_VERSION to 16. 2017-11-22 01:14:58 +02:00
sledgehammer999
e267c2d37a Sync translations from Transifex and run lupdate. 2017-11-22 01:05:19 +02:00
sledgehammer999
ae32edeb26 Correctly check if torrent passed during application start already exists. 2017-11-22 01:05:18 +02:00
Chocobo1
34d38ef466 Fix crash when aborting a torrent creation process. Closes #7783.
The wait time wasn't long enough causing the thread to
terminate prematurely.
Also, to avoid crashing qbt entirely when creating a torrent for a very
big file, I decided to wait indefinitely here.
2017-11-22 01:05:17 +02:00
Chocobo1
120ee6b836 Wrap function into anonymous namespace 2017-11-22 01:05:15 +02:00
Chocobo1
7d25b6fce2 Avoid double delete on close 2017-11-22 01:05:14 +02:00
Chocobo1
068eff9e9f Use Qt5 connect syntax 2017-11-22 01:05:13 +02:00
Chocobo1
31a55f79f1 Remove superfluous QString::fromUtf8() 2017-11-22 01:03:41 +02:00
Chocobo1
bac032e01c Allow drag-n-drop magnet links to mainwindow. Closes #7742. 2017-11-22 01:03:40 +02:00
sledgehammer999
b809941f02 Fix english typo. 2017-11-22 01:03:39 +02:00
Chocobo1
77c3758090 [WebUI] Fix logo missing in login page 2017-11-22 01:03:37 +02:00
luigino
5758817189 qtsingleapplication should always be built statically to avoid dependency problems 2017-11-20 22:48:56 +02:00
Thomas Piccirello
acc9f08a05 Remove duplicate header 2017-11-20 22:48:55 +02:00
Thomas Piccirello
f3b7f17a7c Enable preferences Apply button when ip banlist is modified 2017-11-20 22:48:54 +02:00
Thomas Piccirello
dfc3f047e2 Add ip subnet whitelist for bypassing webui auth 2017-11-20 22:48:53 +02:00
sledgehammer999
223ab7de84 Fix crash on opening torrent/magnet (uninitialized pointer). Closes #7739 #7723. 2017-11-20 22:48:52 +02:00
sledgehammer999
d2a4027347 Improve the github issue template. 2017-11-20 22:48:37 +02:00
sledgehammer999
4594895082 Bump to 4.0.0 2017-11-20 02:43:03 +02:00
sledgehammer999
e457223fcd Update Changelog. 2017-11-20 02:31:42 +02:00
sledgehammer999
8fc25c4524 Sync translations from Transifex and run lupdate. 2017-11-20 02:24:02 +02:00
sledgehammer999
410e133592 Use new logo in the file icon. 2017-11-20 02:10:03 +02:00
sledgehammer999
e114bc7ef6 Use new logo in the splash screen. 2017-11-20 02:10:02 +02:00
sledgehammer999
6ac57cb24c Remove unused image. 2017-11-20 02:10:01 +02:00
sledgehammer999
2b7893adc8 Use the SVG for the tray icon. 2017-11-20 02:10:00 +02:00
sledgehammer999
84b8832d57 Change qbittorrent logo. Issue #6467. 2017-11-20 02:09:59 +02:00
Vladimir Golovnev (Glassez)
0e738b534c Fix WebUI error handling 2017-11-20 02:09:58 +02:00
Chocobo1
96ce8690b6 Simplify AX_BOOST_BASE usage
Regenerate configure
2017-11-20 02:09:57 +02:00
Chocobo1
a23698940c Sync m4 macros with upstream 2017-11-20 02:09:56 +02:00
Chocobo1
50bb733293 [WebAPI] Improve error messages
Also refactor to use class methods
2017-11-20 02:09:55 +02:00
Vladimir Golovnev (Glassez)
6420157b55 Improve RSS events logging
Fix RSS log messages was untranslatable.
Add more logging.
2017-11-20 02:09:54 +02:00
Eugene Shalygin
86bdfbf88c Add option to tune download history list length. Closes #4043. 2017-11-20 02:09:53 +02:00
Eugene Shalygin
81e8f79164 Add maxVisibleItems combo box property to FileSystemPathComboEdit. 2017-11-20 02:09:52 +02:00
Chocobo1
64a0ad33c1 Include/print caught signal in stackdump 2017-11-20 02:09:51 +02:00
Nikolay Korotysh
3cd0ffecaf dropped unneeded executable flag from several files 2017-11-20 02:09:50 +02:00
sledgehammer999
a2ddabaedb Bump API_VERSION to 16. 2017-11-20 02:09:49 +02:00
sledgehammer999
1fec1978aa Update Changelog. 2017-11-20 02:09:48 +02:00
Chocobo1
8de67fd745 Refactor
Add const
Use Qt5 connect syntax
2017-11-20 02:09:47 +02:00
Chocobo1
3b51582416 Explicitly set UPnP state on start-up. Closes #7338.
libtorrent 1.1 enables upnp by default.
2017-11-20 02:09:46 +02:00
sledgehammer999
ffa2fdce9d Change default settings for tracker/tier announces to mimick ìTorrent behavior. 2017-11-20 02:09:45 +02:00
sledgehammer999
588f1c7592 Allow to specify if announcing to all tiers is desired. 2017-11-20 02:09:44 +02:00
Chocobo1
ab1ece2460 Open links in browser. Closes #7651. 2017-11-20 02:09:43 +02:00
sledgehammer999
7a935d8a87 Add Turkish translator in about page. 2017-11-20 02:09:42 +02:00
Chocobo1
3926eba585 Allow SMTP sender to be set. Closes #7575. 2017-11-20 02:09:41 +02:00
Chocobo1
74bf420610 Disable processing events when adding torrents. Closes #7436.
webUI connection timeout & deletion might occur while
doing processEvents() and will result in use-after-free segfault.
2017-11-20 02:09:39 +02:00
Chocobo1
324f18a0b2 [WebUI] Improve log messages 2017-11-20 02:09:38 +02:00
sledgehammer999
c134e391e6 Run lupdate again. 2017-11-20 02:09:37 +02:00
sledgehammer999
24504951b0 Mention more translators in the about page after their request. 2017-11-20 02:09:35 +02:00
sledgehammer999
f7f02ab16a String fixes and optimizations mentioned by translators on Transifex. 2017-11-20 02:09:29 +02:00
sledgehammer999
fe810fcd37 Revert "Update Transifex config file."
This reverts commit e872719ef1.
2017-10-24 01:31:06 +03:00
sledgehammer999
17167e79d2 Sync translations from Transifex and run lupdate. 2017-10-24 01:31:04 +03:00
Chocobo1
7bd86048a8 WebAPI refactor: utilize parseBool() function
Coding style cleanup
Rename variable
Return const reference
Add const
2017-10-24 01:30:37 +03:00
Chocobo1
d399f024a7 Reinitialize webUI server when "IP address" setting changed
An app restart won't be necessary from now on.
2017-10-24 01:30:36 +03:00
sledgehammer999
21f06abef8 Fix row height/width in webui for country flags.
Patch by Chocobo1.
2017-10-24 01:07:57 +03:00
sledgehammer999
fbe0e96fd5 Use svg icons for the country flags.
Closes #6223.
2017-10-24 01:07:55 +03:00
Matthew Fioravante
94e00dd38d Add WebUi\Address config option 2017-10-24 01:07:54 +03:00
Chocobo1
c3f5432877 [WebUI]: Print error messages upon receiving invalid header fields 2017-10-24 01:07:53 +03:00
dzmat
4dcc187a72 speedwidget class: excess QtConcurent usage removal 2017-10-24 01:07:51 +03:00
scootergrisen
97c99dfaaf Add me for danish 2017-10-24 01:07:50 +03:00
sledgehammer999
da83041a3f Generate pngs using svgexport and optimize using PNGGauntlet. 2017-10-24 01:07:49 +03:00
sledgehammer999
d40a4f14dd Optimize and prettify the svg source using svgo. 2017-10-24 01:07:48 +03:00
LordNyriox
de7b0278f4 Invert Framing for Torrent Status Icons
Also recolor the icons to match the text-color used for the torrent-status as well. 
Optimized using SVGOMG [<https://jakearchibald.github.io/svgomg/>].
2017-10-24 01:07:47 +03:00
Chocobo1
17f5e10ffc Convert tab into whitespaces 2017-10-24 01:07:45 +03:00
Chocobo1
a0dbb6c97c WebUI: add optional parameters for /command/download & /command/upload
Specifically:
torrent name: string
download limit, upload limit: number in bytes, default: -1 (unlimited)
sequential download, first last piece prio: boolean true/false, default: false
2017-10-24 01:07:44 +03:00
Chocobo1
4d330a6110 Add uploadLimit, downloadLimit fields to AddTorrentData & AddTorrentParams 2017-10-24 01:07:43 +03:00
Chocobo1
9fc2bf6353 Add file-to-piece-index mappings in /query/propertiesFiles command
Also do the following cleanups:
  Use string constant QB_EXT
  Remove redundant variable
  Add const
2017-10-24 01:07:42 +03:00
sledgehammer999
f9c7121847 Sync translations from Transifex and run lupdate. 2017-10-24 01:07:31 +03:00
Eugene Shalygin
d3a0ac3b6e Make BitTorrent::TorrentState strongly-typed enum
This is needed to forward declare this type and pass it by value.

Conversion from/to QVariant are hanled via Q_DECLARE_METATYPE, while
TorrentState::toString() function was used in webui only and as such is
moved there.
2017-10-24 00:57:58 +03:00
Tim Delaney
a6c99844de Follow project coding style. Issue #2192.
--HG--
branch : magao-dev
2017-10-24 00:57:57 +03:00
Chocobo1
d51a957247 Die gracefully when failed to initialize web server with qbt-nox 2017-10-24 00:57:56 +03:00
Matthew Fioravante
a0c16cd461 Report TCPServer errorString() if webui fails to listen to port 2017-10-24 00:57:55 +03:00
Chocobo1
8fe11dff91 Fix delete key has no effect due to "Ambiguous shortcut overload"
We want to show the accelerator key in the menu but without hitting
the ambiguous overload error.
Fixup of 1378245a63
2017-10-24 00:57:54 +03:00
Eugene Shalygin
efcdcf5898 Update uncrustify config 2017-10-24 00:57:52 +03:00
sledgehammer999
da543cdae2 Optimize SVGs.
Used svgo with commands --pretty --indent=2 --multipass.
2017-10-24 00:55:41 +03:00
sledgehammer999
0374742e57 Optimize PNGs.
Using PNGGauntlet.
2017-10-24 00:55:39 +03:00
sledgehammer999
408052d1ec Remove unused image. Possibly leftover from dropping Qt4. 2017-10-24 00:55:38 +03:00
sledgehammer999
b0ebbc3596 Drop OS/2 support. 2017-10-24 00:55:37 +03:00
sledgehammer999
e45e1166b2 Show new paths in native form. 2017-10-24 00:55:35 +03:00
thalieht
de64d5c3bc Save ratio limits as int instead of string 2017-10-24 00:55:34 +03:00
thalieht
07130c4b26 Coding style, use nullptr and other minor things 2017-10-24 00:55:33 +03:00
thalieht
8482464ad0 Properly pre-select the selected torrent's current ratio limiting options in UpDownRatioDlg dialogs. Fixes #7352 2017-10-24 00:55:32 +03:00
Eugene Shalygin
d7ce6e39d4 cmake: do not use Qt5Widgets when locating QtSingleApplication. Closes #7551.
This fixes cmake builds with GUI disabled and system
QtSingleApplication. We rely on Qt5::Core instead of Qt5::Widgets.
2017-10-24 00:55:30 +03:00
Chocobo1
97acbd5259 Set QTextOption::NoWrap property in "Download from URLs" dialog
This makes it easier to put each magnet link on its own line
2017-10-24 00:55:29 +03:00
Nick Korotysh
60937a1871 show delete accelerator key in menu. closes #7508 2017-10-24 00:55:28 +03:00
Nick Korotysh
ed43bc377d allow search plugins sorting. closes #7526 2017-10-24 00:55:27 +03:00
Vladimir Golovnev (Glassez)
8d11929815 Reformat Windows build configuration files 2017-10-24 00:55:26 +03:00
dzmat
0e6f8c15c5 transferlistwidget class members names clarification 2017-10-24 00:55:24 +03:00
Chocobo1
8107201a5b Fix "Time active" field in transfer list
The "Time active" field was broken, displaying nothing when in
downloading state.
Also change type to int to match the return
types of TorrentHandle::activeTime() & TorrentHandle::seedingTime()
2017-10-24 00:55:23 +03:00
Vladimir Golovnev (Glassez)
395ea4d1d0 Implement TorrentCategoryDialog class 2017-10-24 00:55:22 +03:00
Vladimir Golovnev (Glassez)
7bf317929b Use Qt5 connect() style in CategoryFilterWidget 2017-10-24 00:55:21 +03:00
Chocobo1
3cacf876c9 Show torrent name in "add new torrent" dialog on merging trackers 2017-10-24 00:55:20 +03:00
silver
d6247dd4ec Center Options dialog when showed 2017-10-24 00:53:10 +03:00
silver
4f0c49f1c4 Persist size and treeview header state in preview dialog
- renamed variable m_headerState
 - renamed PreviewSelect class to PreviewSelectDialog
 - renamed previewselect files to previewselectdialog
2017-10-24 00:53:09 +03:00
silver
30455e8b01 Follow project coding style. Issue #2192. 2017-10-24 00:53:08 +03:00
silver
60adb94463 Options dialog save windows state in destructor 2017-10-24 00:53:07 +03:00
Chocobo1
a02fd5b588 Fix last activity calculation. Closes #7461
`time_since_upload` & `time_since_download` can be -1, so filter them out
2017-10-24 00:53:06 +03:00
sledgehammer999
39ce080318 Don't use margins in FileSystemPathEdit widgets.
Introduced in 30081e0.
2017-10-24 00:53:04 +03:00
Chocobo1
f53abd2f07 Update qtsingleapplication
To upstream version a8dda66d7738cde9042b87db27993f710ae3eeeb
2017-10-24 00:53:03 +03:00
Thomas Piccirello
5b0ae0271b Reposition "Priority" menu option in WebUI to match gui (closes #7072) 2017-10-24 00:53:02 +03:00
sledgehammer999
ec2efd8c62 Better reporting of success/failure of torrent and file deletion. 2017-10-24 00:53:01 +03:00
Chocobo1
146daea513 Replace dialog ok-cancel buttons with QDialogButtonBox
This PR will make button order follow the platform default.
For example: windows use: OK, Cancel; linux use: Cancel, OK.
2017-10-24 00:53:00 +03:00
Chocobo1
5ab67faacb Use smaller data type for TriStateBool 2017-10-24 00:52:58 +03:00
Chocobo1
4213d37857 WebAPI: fix addPaused wrong default behavior
Add helper function
Sort include header
2017-10-24 00:52:57 +03:00
Chocobo1
0192922910 Refactor
Merge statements
Use case-insensitive contains()
Add const
Use value(), this avoids inserting empty values.
Use range based for loop
2017-10-24 00:52:56 +03:00
Chocobo1
d2b88e9f84 WebAPI: fix root_folder default behavior
Bug was introduced in
6b33db3ae3
2017-10-24 00:52:55 +03:00
Chocobo1
a32c4aca92 Add comboBox for selecting BitTorrent protocol. Closes #6316.
Use unicode string C_UTP in place of "uTP"
2017-10-24 00:52:54 +03:00
thalieht
91d41336a7 Create root folder option when adding a torrent in WebUI 2017-10-24 00:52:53 +03:00
Chocobo1
d73d790612 Rename option
The previous "Disk write cache size" is not accurate since it is also being used
for read cache, so rename it to "Disk cache".
2017-10-24 00:52:51 +03:00
Chocobo1
af0fed6669 Change default value of m_diskCacheSize. New default is 64 MB. 2017-10-24 00:52:50 +03:00
Eugene Shalygin
a24c13b902 Allow custom tray icons when system icon theme is used. Closes #7403. 2017-10-24 00:52:49 +03:00
Chocobo1
c44c6a8d88 Fix dereferencing freed pointer. Closes #7420.
The torrent is removed from session after `deleteTorrent()`
yet we still invoke `torrent->name()`, thus result in crash.
2017-10-24 00:52:48 +03:00
sledgehammer999
5f62a68e71 Travis: Update libtorrent bottle with boost 1.65+ fix. 2017-10-24 00:52:41 +03:00
Allan Nordhøy
5af90fee46 Spelling: HTTP/HTTPS 2017-10-23 19:03:40 +03:00
Chocobo1
b17566f113 Update coding guidelines
Add ranged-based for loop example
Add class inheritance example
Add Prefer pre-increment, pre-decrement operators section
Fix space after comment keyword
Fix header include order
[skip ci]
2017-10-23 19:03:39 +03:00
sledgehammer999
29edea050b Update Changelog. 2017-10-23 19:03:38 +03:00
sledgehammer999
7ceb646e90 Bump to 3.4.0beta2 2017-10-23 19:03:37 +03:00
sledgehammer999
0ff39e4d10 Sync translations from Transifex and run lupdate. 2017-10-23 19:02:44 +03:00
sledgehammer999
1e146c94bd Fix file list expansion on singlefile torrent with folder. 2017-10-23 18:12:43 +03:00
Vladimir Golovnev
8a0da04807 Fix CategoryFilterWidget::sizeHint() 2017-10-23 18:12:42 +03:00
Vladimir Golovnev
9e7a847cce Fix suggest_mode setting
Actually suggest_mode is of type int.
2017-10-23 18:12:41 +03:00
Chocobo1
63d3f20e51 Sort the resource lists
Update RCC header
2017-10-23 18:12:40 +03:00
Chocobo1
817e3fbb05 Cleanup src.pro 2017-10-23 18:12:39 +03:00
Chocobo1
263e96aba2 Move NOMINMAX define 2017-10-23 18:12:37 +03:00
Chocobo1
0379376fd8 Define QT_USE_QSTRINGBUILDER.
The old defines are deprecated.
QT_USE_QSTRINGBUILDER also supports QByteArray which we use extensively
in WebUI.
2017-10-23 18:12:36 +03:00
Chocobo1
de7efb50c2 Use Qt5 connect syntax 2017-10-23 18:12:35 +03:00
Chocobo1
400f8dc2d8 Setup parent ownership to avoid memory leak 2017-10-23 18:12:34 +03:00
Chocobo1
b2b63be798 Use enums for settings 2017-10-23 18:12:33 +03:00
Chocobo1
c9aba893de Add sanitize helper clampValue() 2017-10-23 18:12:32 +03:00
Chocobo1
1ac4cdcf4d Add send_buffer_watermark send_buffer_low_watermark
send_buffer_watermark_factor knobs
2017-10-23 18:12:30 +03:00
Chocobo1
08a0fef18a Add suggest_mode knob 2017-10-23 18:12:29 +03:00
Chocobo1
6f54c170ab Add choking_algorithm & seed_choking_algorithm knob 2017-10-23 18:12:28 +03:00
Chocobo1
d3b4c7bec4 Add allow_multiple_connections_per_ip knob. Closes #5884. 2017-10-23 18:12:27 +03:00
Chocobo1
f8dfe1ea57 Add mixed_mode_algorithm knob 2017-10-23 18:12:26 +03:00
Chocobo1
ce5f8bab44 Add guided_read_cache knob
cleanup header include order
2017-10-23 18:12:24 +03:00
Vladimir Golovnev
59cf70f8f2 Fix RSS Downloader fails to rename rule
Closes #7333.
2017-10-23 18:12:23 +03:00
Vladimir Golovnev (qlassez)
561975f435 Improve BandwidthScheduler
Don't disable scheduler when manually switching speed limits.
Closes #7306.
2017-10-23 18:12:22 +03:00
Vladimir Golovnev
eae6fea830 Skip user input events when adding torrent
Closes #7327.
2017-10-23 18:12:21 +03:00
Evengard
2673c2b5b2 Disable skipping of loopback interfaces
This fixes the absence of VPN tunnel interfaces under Windows and works around the QTBUG-32349
Fixes #7291
2017-10-23 18:12:20 +03:00
Eugene Shalygin
3c17f3a836 Fix ignoring of incorrect version strings in search plugins. Closes #7101.
Printing of Version with all components set to zero was segfaulting due
to underflow in array index. Also add log message for such plugins.
2017-10-23 18:12:19 +03:00
Chocobo1
0890154e16 WebAPI: fix validating wrong header field. Closes #7311.
X-Forwarded-Host is a foreign proxy setting, it isn't the same as
qbt's local setting and thus it makes no sense to verify it.
2017-10-23 18:12:17 +03:00
sledgehammer999
0877824875 Check for Qt5Svg when configuring. 2017-10-23 18:12:16 +03:00
Chocobo1
60bd5999b0 Wrap class & functions in anonymous namespace
Rename vars
Cleanup class interfaces
Pass by reference whenever possible
2017-10-23 18:12:15 +03:00
sledgehammer999
d0ec60fa01 Switch settings to signed int because libtorrent expects them that way. 2017-10-23 18:12:14 +03:00
sledgehammer999
e7a70a4acc Options to better memory control by libtorrent. Closes #7029. 2017-10-23 18:12:13 +03:00
Eugene Shalygin
85cb49e8e1 Use pixmap cache for file icons on Mac OS and Windows. Closes #7264. 2017-10-23 18:12:12 +03:00
Vladimir Golovnev (qlassez)
3f00a6e5e3 Improve utils/fs.* and fix coding style 2017-10-23 18:12:10 +03:00
vit9696
35e18a2e09 Fix notification display on macOS 2017-10-23 18:12:09 +03:00
sledgehammer999
8ae2ae3b5c Fix broken build. 2017-10-23 18:12:08 +03:00
vit9696
27c5f2aede Fix macOS window restoration after using hide icon 2017-10-23 18:12:07 +03:00
sledgehammer999
34a69aa0b2 Use simpler ifdef style. 2017-10-23 18:12:06 +03:00
Brian Kendall
72fc903f4a Fixed mac specific compiler errors and cleaned up Objective C code
Created new file src/gui/macutilities.mm, moved code from mainwindow.cpp and torrentcontentmodel.cpp that used the Objective C runtime into it and converted it to actual Objective C. Rewrote pixmapForExtension() so that it doesn't call into private Qt functions.
2017-10-23 18:12:05 +03:00
sledgehammer999
4f04992de8 Fix connection problems when a specific interface/ip is configured.
Closes #7235.
Bug related to #7099 and Qt.
2017-10-23 18:12:04 +03:00
vit9696
1b147494d4 Unify preference window borders across the tabs 2017-10-23 18:12:02 +03:00
sledgehammer999
b535a0b44e Fix calculation of 'Average time in queue' stat under libtorrent 1.1.x 2017-10-23 18:12:01 +03:00
Thomas Piccirello
6c2271584c Use single quotes for char.
Use case insensitive compare.
Swap conditionals
2017-10-23 18:12:00 +03:00
Thomas Piccirello
1002b28c95 Add auto torrent management to webui context menu (addresses #6815) 2017-10-23 18:11:54 +03:00
Chocobo1
dfded7bc9d Add space between widgets in left side panel. Closes #7224. 2017-10-23 02:11:55 +03:00
sledgehammer999
36fde9ede5 Error out in configure script if the proper Qt/qmake version isn't found.
Closes #7250.
Partially reverts e64bb1de8c.
2017-10-23 02:11:54 +03:00
Chocobo1
6b4ac1b960 Initialize variables
Remove unused variable
2017-10-23 02:11:53 +03:00
Thomas Piccirello
cc141ba02f Add option to rename torrent from WebUI
Addresses #6815.
2017-10-23 02:11:52 +03:00
Vladimir Golovnev (qlassez)
8fc931a61b Use qUtf8Printable() for logging strings
qDebug(), qInfo(), qWarning(), qCritical(), qFatal() expect %s arguments
to be UTF-8 encoded, while qPrintable() converts to local 8-bit encoding.
Therefore qUtf8Printable() should be used for logging strings instead of
qPrintable().
2017-10-23 02:11:51 +03:00
Vladimir Golovnev (qlassez)
0b6cf54508 Don't remove shared temp folder
Don't remove shared temp folder when torrent finished and moved to
its "complete" folder. Only torrents with stripped root folder
have subfolder in temp folder so they should remove it.
2017-10-23 02:11:50 +03:00
sledgehammer999
ff12163176 Follow project coding style. 2017-10-23 02:11:49 +03:00
sledgehammer999
6a8a0bbd6b Catch possible exceptions when parsing IPs. Closes #7249. 2017-10-23 02:11:47 +03:00
sledgehammer999
f8ebffac65 Use dpiawareness=1 on Windows. Closes #5393. 2017-10-23 02:11:46 +03:00
Vladimir Golovnev (qlassez)
e58f4c0bdf Remove legacy RSS settings after converting
Closes #7226.
2017-10-23 02:11:45 +03:00
thalieht
f450ff278d Remove indentation for category/tag filter widgets in all platforms 2017-10-23 02:11:44 +03:00
Chocobo1
566fd893f4 Prefill torrent name when creating a new torrent. Closes #7229. 2017-10-23 02:11:43 +03:00
sledgehammer999
30ab46999c Fix explicit Torrent Management Mode in Add New Torrent dialog. Closes #5602. 2017-10-23 02:11:35 +03:00
sledgehammer999
0320f9d5b5 Update Changelog. 2017-08-07 08:55:30 +03:00
sledgehammer999
ad7c9ed123 Bump to 3.4.0beta 2017-08-07 08:40:22 +03:00
sledgehammer999
25acdba344 Sync translations from Transifex and run lupdate. 2017-08-07 08:38:09 +03:00
Chocobo1
786059802b Correctly handle translation
Fixup of 07a85a1018
2017-08-07 08:27:09 +03:00
Chocobo1
0ae708114b Move "Copy hash" menu item above "Copy magnet link"
Fix wrong "Copy hash" icon used in webUI
Fixup of 145641ac41
2017-08-07 08:27:08 +03:00
vit9696
fbeaabb841 Fix file type icons not displaying on macOS 2017-08-07 08:27:07 +03:00
Tom Piccirello
98bef605a7 Set torrent location from webui context menu (addresses #6815) (#7062)
* Add option to set torrent location from webui context menu (addresses #6815)

* Update debug messages

* Use logger

* Remove redundant curly braces

* Remove message

* Use log message from transferlistwidget

* Use QDir

* Remove unused import

* Check if newLocation is an empty string
2017-08-07 08:27:05 +03:00
Tom Piccirello
37a0e48b46 Add copy options to webui context menu (addresses #6815) (#7036)
* Add copy options to webui context menu
Add Copy Hash to gui (closes #6964)

* Use switch statement

* Use camel case, switch from signal to qaction.

* Rename variable

* Change variable name
2017-08-07 08:27:04 +03:00
sledgehammer999
fea1a66aba Fix some more strings. 2017-08-07 08:27:03 +03:00
Allan Nordhøy
0ffdb51f95 display, URLs, esc ' 2017-08-07 08:27:01 +03:00
Vladimir Golovnev (qlassez)
4e596629fd Don't replace existing files when relocating torrent 2017-08-07 08:27:00 +03:00
Vladimir Golovnev (qlassez)
61281dd226 Don't remove shared temp folder 2017-08-07 08:26:59 +03:00
Vladimir Golovnev (Glassez)
61d1f2180f Don't create subfolder inside temp folder 2017-08-07 08:26:58 +03:00
vlakoff
3bcf941205 Windows installer: also detect running process when it is 64-bit
Replacing FindProcDLL with a different one that can be found here:

http://forums.winamp.com/showthread.php?t=322583#post2777719
2017-08-07 08:26:56 +03:00
Tony Gregerson
1746c9d331 Improve checkbox interface for selecting tags in the context menu. Closes #7060 2017-08-07 08:26:55 +03:00
Vladimir Golovnev (Glassez)
58c31c5353 Fix temporary subfolder isn't deleted 2017-08-07 08:26:47 +03:00
sledgehammer999
e872719ef1 Update Transifex config file. 2017-07-27 19:19:10 +03:00
770 changed files with 147728 additions and 290361 deletions

View File

@@ -45,7 +45,8 @@ before_build:
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
- SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom;
# setup project
- COPY /Y "%CACHE_DIR%\conf.pri" "%REPO_DIR%"
- COPY /Y "%CACHE_DIR%\winconf.pri" "%REPO_DIR%"
- COPY /Y "%CACHE_DIR%\winconf-msvc.pri" "%REPO_DIR%"
# workarounds
- MKLINK /J "c:\qbt\base" "%CACHE_DIR%\base"

1
.gitignore vendored
View File

@@ -22,7 +22,6 @@ qrc_*.cpp
ui_*.h
*.moc
src/lang/qbittorrent_*.qm
src/webui/www/translations/webui_*.qm
.DS_Store
.qmake.stash
src/qbittorrent.app

View File

@@ -3,8 +3,7 @@ language: cpp
os:
- linux
- osx
dist: xenial
osx_image: xcode7.3
env:
matrix:
@@ -39,6 +38,11 @@ cache:
directories:
- $HOME/hombebrew_cache
# opt-in Ubuntu Trusty
dist: trusty
# container-based builds
sudo: false
addons:
coverity_scan:
project:
@@ -50,30 +54,34 @@ addons:
notification_email: sledgehammer999@qbittorrent.org
apt:
sources:
# sources list: https://github.com/travis-ci/apt-source-safelist/blob/master/ubuntu.json
# sources list: https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
- ubuntu-toolchain-r-test
#- boost-latest
- 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-safelist/blob/master/ubuntu-trusty
# packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
- [autoconf, automake, colormake]
- [ninja-build]
- [cmake, ninja-build]
- libssl-dev
- [libboost-dev, libboost-system-dev]
- libtorrent-rasterbar-dev
- [qtbase5-dev, qttools5-dev-tools, libqt5svg5-dev]
- [qt55base, qt55svg, qt55tools]
- [gcc-6, g++-6]
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 -j2" # Using nprocs/2 sometimes may fail (gcc is killed by system)
- alias make="colormake -j3" # 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" PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH"
CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedefs -Wno-inconsistent-missing-override"
qbtconf="$qbtconf --prefix="$qbt_path""
fi
# options for specific branches
@@ -82,6 +90,14 @@ 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
@@ -105,24 +121,41 @@ install:
# dependencies
brew update > /dev/null
brew outdated "pkg-config" || brew upgrade "pkg-config"
brew install colormake ccache zlib qt openssl libtorrent-rasterbar
brew install colormake ccache zlib qt
PATH="/usr/local/opt/ccache/libexec:$PATH"
brew link --force zlib qt
wget https://builds.shiki.hu/homebrew/version
if ! cmp --quiet "version" "$HOME/hombebrew_cache/version" ; then
echo "Cached files are different from server. Downloading new ones."
# First delete old files
rm -r "$HOME/hombebrew_cache"
mkdir "$HOME/hombebrew_cache"
cp "version" $HOME/hombebrew_cache
cd "$HOME/hombebrew_cache"
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.1.6+git20180101.b45acf28a5+patched-configure.el_capitan.bottle.tar.gz
fi
# Copy custom libtorrent bottle to homebrew's cache so it can find and install it
# Also install our custom libtorrent formula by passing the local path to it
# These 2 files are restored from Travis' cache.
cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.1.6+git20180101.b45acf28a5+patched-configure.el_capitan.bottle.tar.gz" "$(brew --cache)"
brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb"
if [ "$build_system" = "cmake" ]; then
brew outdated cmake || brew upgrade cmake
brew install ninja
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/"
ln -s /usr/local/opt/qt/mkspecs /usr/local/mkspecs
ln -s /usr/local/opt/qt/plugins /usr/local/plugins
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
@@ -133,15 +166,21 @@ script:
if [ "$build_system" = "cmake" ]; then
mkdir build
cd build
if [ "$gui" = "false" ]; then
DISABLE_GUI_OPTION="-DCMAKE_DISABLE_FIND_PACKAGE_Qt5Widgets=ON"
fi
cmake $DISABLE_GUI_OPTION -DCMAKE_INSTALL_PREFIX="$qbt_path" "$MY_CMAKE_OPENSSL_HINT" \
cmake -DGUI=${gui} -DCMAKE_INSTALL_PREFIX="$qbt_path" "$MY_CMAKE_OPENSSL_HINT" \
-G "Ninja" -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=TRUE ..
BUILD_TOOL="ninja"
fi
if [ "$build_system" = "qmake" ]; then
./bootstrap.sh && ./configure $qbtconf CXXFLAGS="$CXXFLAGS"
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
# For some reason for RC_1_1 we need to also specify the OpenSSL compiler/linker flags
# Homebrew doesn't symlink OpenSSL for security reasons
./bootstrap.sh && ./configure $qbtconf CXXFLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --cflags openssl)" LDFLAGS="$(PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" pkg-config --libs openssl)"
sed -i "" -e "s/^\(CC.*&&\).*$/\1 $CC/" src/Makefile # workaround for Qt & ccache: https://bugreports.qt.io/browse/QTBUG-31034
sed -i "" -e "s/^\(CXX.*&&\).*$/\1 $CXX/" src/Makefile
sed -i "" -e 's/^\(CXXFLAGS.*\)$/\1 -Wno-unused-local-typedefs -Wno-inconsistent-missing-override/' src/Makefile
else
./bootstrap.sh && ./configure $qbtconf
fi
BUILD_TOOL="make"
fi
- $BUILD_TOOL && $BUILD_TOOL install

View File

@@ -10,18 +10,10 @@ type = QT
minimum_perc = 23
mode = developer
[qbittorrent.qbittorrentdesktop_master]
source_file = dist/unix/org.qbittorrent.qBittorrent.desktop
source_file = src/icons/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

View File

@@ -1,6 +1,5 @@
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
message(AUTHOR_WARNING "If the build fails, please try the autotools/qmake method.")
cmake_minimum_required(VERSION 3.5)
cmake_policy(VERSION 3.5)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
include(FunctionReadVersion)
@@ -26,29 +25,32 @@ add_definitions(-DQBT_VERSION_BUILD=${VER_BUILD})
add_definitions(-DQBT_VERSION="v${PROJECT_VERSION}")
add_definitions(-DQBT_VERSION_2="${PROJECT_VERSION}")
include(GNUInstallDirs)
include(FeatureSummary)
# version requirements
set(requiredBoostVersion 1.35)
set(requiredQtVersion 5.5.1)
if (UNIX AND NOT APPLE)
include(GNUInstallDirs)
endif (UNIX AND NOT APPLE)
if(WIN32)
include(winconf)
endif(WIN32)
# we need options here, because they are used not only in "src" subdir, but in the "dist" dir too
include(CMakeDependentOption)
# we need options here, at the top level, because they are used not only in "src" subdir, but in the "dist" dir too
include(CompileFeature)
option(SYSTEM_QTSINGLEAPPLICATION
"Use the system qtsingleapplication library or shipped one otherwise")
option(GUI "Allows to disable GUI for headless running. Disables QtDBus and the GeoIP Database" ON)
option(WEBUI "Allows to disable the WebUI." ON)
if (WIN32)
option(STACKTRACE_WIN "")
else (WIN32)
cmake_dependent_option(SYSTEMD "Install the systemd service file (headless only)" OFF
"NOT GUI" OFF)
cmake_dependent_option(DBUS "Enable use of QtDBus (GUI only)" ON "GUI" OFF)
endif(WIN32)
optional_compile_definitions(COUNTRIES_RESOLUTION FEATURE DESCRIPTION "Enable resolving peers IP addresses to countries"
DEFAULT ON DISABLED DISABLE_COUNTRIES_RESOLUTION)
optional_compile_definitions(STACKTRACE FEATURE DESCRIPTION "Enable stacktraces"
DEFAULT ON ENABLED STACKTRACE)
optional_compile_definitions(WEBUI FEATURE DESCRIPTION "Enables built-in HTTP server for headless use"
DEFAULT ON DISABLED DISABLE_WEBUI)
add_subdirectory(src)
add_subdirectory(dist)
feature_summary(DESCRIPTION "\nConfiguration results:" WHAT ALL)

View File

@@ -1,38 +1,10 @@
All new code **must** follow the following coding guidelines.
If you make changes in a file that still uses another coding style, make sure that you follow these guidelines for your changes.
For programming languages other than C++ (e.g. JavaScript) used in this repository and submodules, unless otherwise specified, coding guidelines listed here applies as much as possible.
All new code must follow the following coding guidelines.
If you make changes in a file that still uses another coding style, make sure that you follow these guidelines for your changes instead.
**Note 1:** I will not take your head if you forget and use another style. However, most probably the request will be delayed until you fix your coding style.
**Note 2:** You can use the `uncrustify` program/tool to clean up any source file. Use it with the `uncrustify.cfg` configuration file found in the root folder.
**Note 3:** There is also a style for QtCreator but it doesn't cover all cases. In QtCreator `Tools->Options...->C++->Code Style->Import...` and choose the `codingStyleQtCreator.xml` file found in the root folder.
### Table Of Contents
* [1. New lines &amp; curly braces](#1-new-lines--curly-braces)
* [a. Function blocks, class/struct definitions, namespaces](#a-function-blocks-classstruct-definitions-namespaces)
* [b. Other code blocks](#b-other-code-blocks)
* [c. Blocks in switch's case labels](#c-blocks-in-switchs-case-labels)
* [d. If-else statements](#d-if-else-statements)
* [e. Single statement if blocks](#e-single-statement-if-blocks)
* [f. Acceptable conditions to omit braces](#f-acceptable-conditions-to-omit-braces)
* [g. Brace enclosed initializers](#g-brace-enclosed-initializers)
* [2. Indentation](#2-indentation)
* [3. File encoding and line endings](#3-file-encoding-and-line-endings)
* [4. Initialization lists](#4-initialization-lists)
* [5. Enums](#5-enums)
* [6. Names](#6-names)
* [a. Type names and namespaces](#a-type-names-and-namespaces)
* [b. Variable names](#b-variable-names)
* [c. Private member variable names](#c-private-member-variable-names)
* [7. Header inclusion order](#7-header-inclusion-order)
* [8. Include guard](#8-include-guard)
* [9. Misc](#9-misc)
* [10. Git commit message](#10-git-commit-message)
* [11. Not covered above](#11-not-covered-above)
---
### 1. New lines & curly braces ###
### 1. Curly braces ###
#### a. Function blocks, class/struct definitions, namespaces ####
```c++
int myFunction(int a)
@@ -117,8 +89,18 @@ default:
}
```
#### d. If-else statements ####
The `else if`/`else` must be on their own lines:
#### d. Brace enclosed initializers ####
Unlike single-line functions, you must not insert spaces between the brackets and concluded expressions.<br/>
But you must insert a space between the variable name and initializer.
```c++
Class obj {}; // empty
Class obj {expr};
Class obj {expr1, /*...,*/ exprN};
QVariantMap map {{"key1", 5}, {"key2", 10}};
```
### 2. If blocks ###
#### a. Multiple tests ####
```c++
if (condition) {
// code
@@ -130,71 +112,40 @@ else {
// code
}
```
The `else if`/`else` must be on their own lines.
#### e. Single statement if blocks ####
Most single statement if blocks should look like this:
#### b. Single statement if blocks ####
**Most** single statement if blocks should look like this:
```c++
if (condition)
a = a + b;
```
One acceptable exception to this can be `return`, `break` or `continue` statements,
provided that the test condition isn't very long and its body statement occupies only one line.
However you can still choose to use the first rule.
One acceptable exception to this **can be** `return`, `break` or `continue` statements, provided that the test condition isn't very long. However you can choose to use the first rule instead.
```c++
if (a > 0) return;
a = myFunction();
b = a * 1500;
while (p) {
// ...
if (!b) continue;
}
if (b > 0) return;
c = 100 / b;
```
#### f. Acceptable conditions to omit braces ####
When the conditional statement in `if`/`else` has only one line and its body occupy only one line,
this also applies to loops statements.
Notice that for a series of `if - else` branches, if one branch needs braces then all branches must add braces.
```c++
if (a < b) // conditional statement
do(a); // body
#### c. Using curly braces for single statement if blocks ####
if (a < b)
do(a);
else if (a > b)
do(b);
else
do(c);
However, there are cases where curly braces for single statement if blocks **should** be used.
* If some branch needs braces then all others should use them. Unless you have multiple `else if` in a row and the one needing the braces is only for a very small sub-block of code.
* Another exception would be when we have nested if blocks or generally multiple levels of code that affect code readability.
if (a < b) {
do(a);
}
else if (a > b) { // curly braces required here, then all branches should also add them
do(b);
do(d);
}
else {
do(c);
}
```
Generally it will depend on the particular piece of code and would be determined on how readable that piece of code is. **If in doubt** always use braces if one of the above exceptions applies.
#### g. Brace enclosed initializers ####
Unlike single-line functions, you must not insert spaces between the brackets and concluded expressions.<br/>
But you must insert a space between the variable name and initializer.
```c++
Class obj {}; // empty
Class obj {expr};
Class obj {expr1, /*...,*/ exprN};
QVariantMap map {{"key1", 5}, {"key2", 10}};
```
### 2. Indentation ###
### 3. Indentation ###
4 spaces.
### 3. File encoding and line endings ###
### 4. File encoding and line endings. ###
UTF-8 and Unix-like line ending (LF). Unless some platform specific files need other encodings/line endings.
### 4. Initialization lists ###
### 5. Initialization lists. ###
Initialization lists should be vertical. This will allow for more easily readable diffs. The initialization colon should be indented and in its own line along with first argument. The rest of the arguments should be indented too and have the comma prepended.
```c++
myClass::myClass(int a, int b, int c, int d)
@@ -207,7 +158,7 @@ myClass::myClass(int a, int b, int c, int d)
}
```
### 5. Enums ###
### 6. Enums. ###
Enums should be vertical. This will allow for more easily readable diffs. The members should be indented.
```c++
enum Days
@@ -222,7 +173,7 @@ enum Days
};
```
### 6. Names ###
### 7. Names. ###
All names should be camelCased.
#### a. Type names and namespaces ####
@@ -256,73 +207,40 @@ class MyClass
}
```
### 7. Header inclusion order ###
The headers should be placed in the following group order:
1. Module header (in .cpp)
2. C++ Standard Library headers
3. System headers
4. Boost library headers
5. Libtorrent headers
6. Qt headers
7. qBittorrent's own headers, starting from the *base* headers.
The headers should be ordered alphabetically within each group.
If there are conditionals for the same header group, then put them at the bottom of the respective group.
If there are conditionals that contain headers from several different header groups, then put them above the "qBittorrent's own headers" group.
One exception is the header containing the library version (for example, QtGlobal), this particular header isn't constrained by the aforementioned order.
### 8. Header inclusion order. ###
The headers should be placed in the following order:
1. Module header (in .cpp)
2. System/Qt/Boost etc. headers (splitted in subcategories if you have many).
3. Application headers, starting from *Base* headers.
The headers should be ordered alphabetically within each group (subgroup).<br/>
<br/>
Example:
```c++
// file: examplewidget.cpp
// examplewidget.cpp
// Module header
#include "examplewidget.h"
// exceptions, headers containing version number
#include <boost/version.hpp>
#include <libtorrent/version.hpp>
#include <QtGlobal>
// C++ Standard Library headers
#include <cmath>
#include <cstdio>
#ifdef Q_OS_WIN // conditional
#include <cmath>
#endif
// System headers
#ifdef Q_OS_WIN
#include <Windows.h>
#endif
// Boost library headers
#include <boost/circular_buffer.hpp>
// Libtorrent headers
#include <libtorrent/session.hpp>
// Qt headers
#include <QDateTime>
#include <QList>
#include <QString>
#include <QUrl>
#ifdef Q_OS_MAC // conditional
#include <QFont>
#endif
#include <libtorrent/version.hpp>
// conditional that contains headers from several different header groups
#if LIBTORRENT_VERSION_NUM >= 10100
#include <memory>
#include <QElapsedTimer>
#endif
// qBittorrent's own headers
#include "base/bittorrent/infohash.h"
#include "anothermodule.h"
#include "base/bittorrent/session.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "ui_examplewidget.h"
```
### 8. Include guard ###
### 9. Include guard. ###
`#pragma once` should be used instead of "include guard" in new code:
```c++
// examplewidget.h
@@ -338,7 +256,7 @@ class ExampleWidget : public QWidget
```
### 9. Misc ###
### 10. Misc. ###
* Line breaks for long lines with operation:
@@ -414,16 +332,5 @@ i++, j--; // No
* Method definitions aren't allowed in header files
### 10. Git commit message ###
1. Limit the subject line to 50 characters. Subject should contain only the very essence of the changes (you should avoid extra details and internals)
2. Separate subject from body with a blank line
3. Capitalize the subject line
4. Do not end the subject line with a period
5. Use the imperative mood in the subject line (it's like you're ordering the program to do something (e.g. "Don't create temporary substrings")
6. Wrap the body at 72 characters
7. Use the body to explain what and why vs. how
8. If commit fixes a reported issue, mention it in the message body (e.g. `Closes #4134.`)
### 11. Not covered above ###
If something isn't covered above, just follow the same style the file you are editing has.
*This guide is not exhaustive and the style for a particular piece of code not specified here will be determined by project members on code review.*
### 10. Not covered above ###
If something isn't covered above, just follow the same style the file you are editing has. If that particular detail isn't present in the file you are editing, then use whatever the rest of the project uses.

View File

@@ -1,221 +1,35 @@
# How to contribute to qBittorrent
# Filing an issue
### Must read
* If you aren't sure, you can ask on the [**forum**](http://forum.qbittorrent.org) or read our [**wiki**](http://wiki.qbittorrent.org) first.
* Do a quick **search**. Others might already reported the issue.
* Write in **English**!
* Provide **version** information: (You can find version numbers at menu `Help -> About -> Libraries`)
```
qBittorrent:
Qt:
libtorrent:
boost:
OS version:
```
* Provide **steps** to reproduce the problem, it will be easier to pinpoint the fault.
* **Screenshots**! A screenshot is worth a thousand words. just upload it. [(How?)](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests)
### Good to know
* **Be patient**. The dev team is small and resource limited. Devs finding their free time, analyzing the problem and fixing the issue, it all takes time. :clock3:
* If you can code, why not become a **contributor** by fixing the issue and open a pull request? :wink:
* Harsh words or threats won't help your situation. What's worse, your complain will (very likely) to be **ignored**. :fearful:
There are three main ways to contribute to the project.
Read the respective section to find out more.
### Table Of Contents
* **[Bug reporting etiquette](#bug-reporting-etiquette)**
* **[Submitting an issue/bug report](#submitting-an-issuebug-report)**
* [What is an actual bug report?](#what-is-an-actual-bug-report)
* [Before submitting a bug report](#before-submitting-a-bug-report)
* [Steps to ensure a good bug report](#steps-to-ensure-a-good-bug-report)
* **[Suggesting enhancements/feature requests](#suggesting-enhancementsfeature-requests)**
* [Before submitting an enhancement/feature request](#before-submitting-an-enhancementfeature-request)
* [Steps to ensure a good enhancement/feature suggestion](#steps-to-ensure-a-good-enhancementfeature-suggestion)
* **[Opening a pull request](#opening-a-pull-request)**
* [Must read](#must-read)
* [Good to know](#good-to-know)
# Bug reporting etiquette
* Issues, pull requests, and comments must always be in **English.**
* This project is supported by volunteers, do not expect "customer support"-style interaction.
* **Be patient.** The development team is small and resource limited. Developers and contributors take from their free time to analyze the problem and fix the issue. :clock3:
* Harsh words or threats won't help your situation. What's worse, your complain will (very likely) be **ignored.** :fearful:
# Submitting an issue/bug report
This section guides you through submitting an issue/bug report for qBittorrent.
Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports.
Make sure to follow these rules carefully when submitting a bug report. Failure to do so will result in the issue being closed.
## What is an actual bug report?
Developers and contributors are not supposed to deal with issues for which little to no investigation to find the actual cause of a purported issue was made by the reporter.
Positive contributions are those which are reported with efforts to find the actual cause of an issue, or at the very least efforts were made to narrow it as much as possible.
Requiring people to investigate as much as possible before opening an issue will more than likely avoid burdening the project with invalid issues or issues unrelated to qBittorrent.
The following are _not_ bug reports. **Check the [wiki][wiki-url], [forum][forum-url] or other places for help and support for issues like these**:
- Explanation of qBittorrent options (see [wiki][wiki-url]).
- Help with WebUI setup.
- Help with embedded tracker setup.
- Help about BitTorrent in general.
- Issues with specific search plugins.
- Asking for specific builds of qBittorrent other than the current one. You can install older releases at your own risk or for regression testing purposes. Previous Windows and macOS builds are available [here][builds-url].
- If you want older Linux builds, you will have to compile them yourself from the corresponding commits, or ask someone on the [forum][forum-url] to do it for you.
- Possibly others. Read on and use common sense.
The issue tracker is for provable issues only: You will have to make the case that the issue is really with qBittorrent and not something else on your side.
To make a case means to provide detailed steps so that anybody can reproduce the issue.
Be sure to rule out that the issue is not caused by something specific on your side.
Issue reports for bugs that apparently aren't easily reproducible or that you can't figure out what triggers it even though you tried are OK.
Any issue opened without effort to provide the required details for developers, contributors or anybody else to reproduce the problem will be closed as invalid.
For example:
- Crash reports with just a stack trace.
- Speculated performance issues that do not come with actual profiling data + analysis supporting the claim.
## Before submitting a bug report
- **Do some basic troubleshooting (examples)**:
- Restart qBittorrent.
- Restart your PC.
- Update your OS (e.g. Windows updates).
- Update your network card drivers.
- Fully reinstall qBittorrent.
- etc...
- Make sure the problem is not caused by anti-virus or other program messing with your files.
- Check if you can reproduce the problem in the latest version of qBittorrent.
- **Check [forum][forum-url] and [wiki][wiki-url].** You might be able to find the cause of the problem and fix things yourself.
- **Check if the issue exists already in the issue tracker.**
- If it does and the issue is still open, add a comment to the existing issue instead of opening a new one.
- If you find a Closed issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
- If the issue is with the search functionality:
- **Make sure you have [`python`][python-url] installed correctly (remember the search functionality requires a working python installation).**
- Make sure it is in fact a problem with the search functionality itself, and not a problem with the plugins. If something does not work properly with the search functionality, the first step is to rule out search plugin-related issues.
- For search plugin issues, report on the respective search plugin support page, or at [qbittorrent/search-plugins][search-plugins-url].
## Steps to ensure a good bug report
**Follow these guidelines** in order to provide as much useful information as possible right away. Not all of them are applicable to all issues, but you are expected to follow most of these steps (use common sense).
Otherwise, we've noticed that a lot of your time (and the developers') gets thrown away on exchanging back and forth to get this information.
* Use a **clear and descriptive title** for the issue to identify the problem.
* Post only **one specific issue per submission.**
* **Fill out the issue template properly.**
- **Make sure you are using qBittorrent on a supported platform.** Do not submit issues which can only be reproduced on beta/unsupported releases of supported operating systems (e.g. Windows 10 Insider, Ubuntu 12.04 LTS, etc).
These are unstable/unsupported platforms, and in all likelihood, whatever the issue is, it is not related to qBittorrent.
* **Specify the OS you're using, its version and architecture.**
* Examples: Windows 8.1 32-bit, Linux Mint 17.1 64-bit, Windows 10 Fall creators Update 64-bit, etc.
* **Report only if you run into the issue with an official stable release, a beta release, or with the most recent upstream changes (in this last case specify the specific commit you are on).** (beta testing is encouraged :smile:). We do not provide support for bugs on unofficial Windows builds.
* **Specify the version of qBittorrent** you are using, as well as its **architecture** (x86 or x64) and its **libraries' versions** (Help -> About -> Libraries).
* Specify **how you installed**:
- Linux: either from the PPA, your distribution's repositories, or compiled from source, or even possibly third-party repositories.
- Windows: either from the installer, or compiled from source, or even possibly third-party repositories.
- macOS: either from the installer, or compiled from source, or even possibly third-party repositories.
* **Describe the exact steps which reproduce the problem in as many details as possible.**
- For example, start by explaining how you started qBittorrent, e.g. was it via the terminal? Desktop icon? Did you start it as root or normal user?
- **When listing steps, don't just say what you did, but explain how you did it.**
- For example, if you added a torrent for download, did you do so via a `.torrent` file or via a magnet link? If it was with a torrent file did you do so by dragging the torrent file from the file manager to the transfer list, or did you use the "Add Torrent File" in the Top Bar?
- Describe the behavior you observed after following the steps and point out what exactly is the problem with that behavior; this is what we'll be looking for after executing the steps.
* **Explain which behavior you expected to see instead** and why.
* Use **screenshots/animated GIFs to help describe the issue** whenever appropriate [(How?)][attachments-howto-url].
* If the problem wasn't triggered by a specific action, describe what you were doing before the problem happened.
* **If you are reporting that qBittorrent crashes**, include the stack trace in the report; include it in a code block, a file attachment, or put it in a gist and provide link to that gist.
* **For performance-related issues**, include as much profiling data as you can (resource usage graphs, etc).
* Paste the **qBittorrent log** (or put the contents of the log in a gist and provide a link to the gist). The log can be viewed in the GUI (View -> Log -> tick all boxes). If you can't do that, the file is at:
- Linux: `~/.local/share/data/qBittorrent/logs/qBittorrent.log`
- Windows: `%LocalAppData%\qBittorrent\logs`
- macOS: `~/Library/Application Support/qBittorrent/qBittorrent.log`
* **Do NOT post comments like "+1" or "me too!"** without providing new relevant info on the issue. Using the built-in reactions is OK though. Remember that you can use the "subscribe" button to receive notifications of that report without having to comment first.
* If there seems to be an **issue with specific torrent files/magnet links**:
- Don't post private `.torrent` files/magnet links, or ones that point to copyrighted content. If you are willing, offer to email a link or the `.torrent` file itself to whoever developer is debugging it and requests it.
- Make sure you can't reproduce the problem with another client, to rule out the possibility that the issue is with the `.torrent` file/magnet link itself.
* A screenshot, transcription or file upload of any of **qBittorrent's preferences that differ from the defaults.** Please include everything different from the defaults whether or not it seems relevant to your issue.
* **Attachment rules**:
- Short logs and error messages can be pasted as quotes/code whenever small enough; otherwise make a gist with the contents and post the link to the gist.
- Avoid linking/attaching impractical file formats such as PDFs/Word documents with images. If you want to post an image, just post the image.
### Provide more context by answering these questions (if applicable):
- Can you **reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens (e.g. only happens with extremely large torrents/only happens after qBittorrent is open for more than 2 days/etc...)
- Did the problem start happening recently (e.g. after updating to a new version of qBittorrent) or was this always a problem?
- If the problem started happening recently, can you reproduce the problem in an older version of qBittorrent?
- Are you saving files locally (in a disk in your machine), or are you saving them remotely (e.g. network drives)?
- Are you using qBittorrent with multiple monitors? If so, can you reproduce the problem when you use a single monitor?
Good read: [How to Report Bugs Effectively][howto-report-bugs-url]
# Suggesting enhancements/feature requests
This section guides you through submitting an enhancement suggestion for qBittorrent, including completely new features and minor improvements to existing functionality.
Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions.
## Before submitting an enhancement/feature request
* Check the [wiki][wiki-url] and [forum][forum-url] for tips — you might discover that the enhancement is already available.
* Most importantly, check if you're using the latest version of qBittorrent and if you can get the desired behavior by changing qBittorrent's settings.
* Check in the [releases][releases-url] page or on the [forum][forum-url], see if there's already a alpha/beta version with that enhancement.
* Perform a cursory search to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
## Steps to ensure a good enhancement/feature suggestion
- Specify which version of qBittorrent you're using.
- Specify the name and version of the OS you're using.
- Provide a step-by-step description of the suggested enhancement in as many details as possible.
- Describe the current behavior and explain which behavior you expected to see instead and why.
- Include screenshots and animated GIFs which help you demonstrate the steps or point out the part of qBittorrent which the suggestion is related to.
- If this enhancement exists in other BitTorrent clients, list those clients.
# Opening a pull request
### Must read
* Read our [**coding guidelines**][coding-guidelines-url]. There are some scripts to help you: [uncrustify script][uncrustify-script-url], [astyle script][astyle-script-url], [(related thread)][coding-guidelines-thread-url].
* Keep the title **short** and provide a **clear** description about what your pull request does.
* Provide **screenshots** for UI related changes.
* Keep your git commit history **clean** and **precise.** Refer to the section about "Git commit messages" in the [**coding guidelines**][coding-guidelines-url].
* If your commit fixes a reported issue (for example #4134), add the following message to the commit `Closes #4134.`. Example [here][commit-message-fix-issue-example-url].
* Read our [**coding guidelines**](https://github.com/qbittorrent/qBittorrent/blob/master/CODING_GUIDELINES.md). There are some scripts to help you: [uncrustify script](https://raw.githubusercontent.com/qbittorrent/qBittorrent/master/uncrustify.cfg), [astyle script](https://gist.github.com/Chocobo1/539cee860d1eef0acfa6), [(related thread)](https://github.com/qbittorrent/qBittorrent/issues/2192).
* Keep the title **short** and provide a **clear** description about what your pull request does.
* Provide **screenshots** for UI related changes.
* Keep your git commit history **clean** and **precise**. Commits like `xxx fixup` should not appear.
* If your commit fix a reported issue (for example #4134), add the following message to the commit `Closes #4134.`. Example [here](https://github.com/qbittorrent/qBittorrent/commit/a74bac20c4e8de9776bf9bb77fdc7526135d1988).
### Good to know
* **Search** pull request history! Others might have already implemented your idea and it is waiting to be merged (or got rejected already). Save your precious time by doing a search first.
* When resolving merge conflicts, do `git rebase <target_branch_name>`, don't do `git pull`. Then you can start fixing the conflicts. Here is a good explanation: [link][merging-vs-rebasing-url].
[astyle-script-url]: https://gist.github.com/Chocobo1/539cee860d1eef0acfa6
[attachments-howto-url]: https://help.github.com/articles/file-attachments-on-issues-and-pull-requests
[coding-guidelines-url]: https://github.com/qbittorrent/qBittorrent/blob/master/CODING_GUIDELINES.md
[coding-guidelines-thread-url]: https://github.com/qbittorrent/qBittorrent/issues/2192
[commit-message-fix-issue-example-url]: https://github.com/qbittorrent/qBittorrent/commit/c07cd440cd46345297debb47cb260f8688975f50
[forum-url]: http://forum.qbittorrent.org/
[howto-report-bugs-url]: https://www.chiark.greenend.org.uk/~sgtatham/bugs.html
[merging-vs-rebasing-url]: https://www.atlassian.com/git/tutorials/merging-vs-rebasing
[python-url]: https://www.python.org/
[releases-url]: https://github.com/qbittorrent/qBittorrent/releases
[search-plugins-url]: https://github.com/qbittorrent/search-plugins
[uncrustify-script-url]: https://raw.githubusercontent.com/qbittorrent/qBittorrent/master/uncrustify.cfg
[wiki-url]: https://github.com/qbittorrent/qBittorrent/wiki
[builds-url]: https://sourceforge.net/projects/qbittorrent/files/
* **Search** pull request history! Others might already implemented your idea and is waiting to be merged (or got rejected already). Save your precious time by doing a search first.
* When resolving merge conflicts, do `git rebase <target_branch_name>`, don't do `git pull`. Then you can start fixing the conflicts. Here is a good explanation: [link](https://www.atlassian.com/git/tutorials/merging-vs-rebasing).

328
Changelog
View File

@@ -1,289 +1,3 @@
* 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)
- FEATURE: Clear LineEdit on ESC (silverqx)
- BUGFIX: Fix divide-by-zero crash (Chocobo1)
- BUGFIX: Remove speed limit checkbox in Options dialog (Chocobo1)
- BUGFIX: Fix speed graph "high speeds" bug (dzmat)
- BUGFIX: Don't update torrent status unnecessarily (glassez)
- BUGFIX: Improve force recheck of paused torrent (glassez)
- BUGFIX: Restore torrent in two steps (glassez)
- BUGFIX: Improve scaling of speed graphs (dzmat)
- BUGFIX: Add isNetworkFileSystem() detection on Windows. This allows network mounts to be monitored correctly by polling timer. (Chocobo1)
- BUGFIX: Reduce horizontal graphs resolution. Improves perfomance. (dzmat)
- BUGFIX: Don't recheck just checked torrent (mj-p, glassez)
- BUGFIX: Add SMB2 magic number (Chocobo1)
- BUGFIX: Restore startup perfomance to v4.1.2 times. Needs at least libtorrent 1.1.10 (sledgehammer999)
- BUGFIX: Make strings actually translatable (sledgehammer999)
- WEBUI: Handle downloading .torrent file as success (Tom Piccirello)
- WEBUI: Fix Alternative Web UI to be available (glassez)
- WEBUI: Consider empty locale setting as not set (glassez)
- WEBUI: Add free disk space to WebUI status bar (Thomas Piccirello)
- WEBUI: Add WebUI search API controller (Thomas Piccirello)
- WEBUI: Fix WebUI Auto TMM context menu bug (Thomas Piccirello)
- WEBUI: Use independent translation for WebUI (glassez)
- WEBUI: Add categories WebAPI (Thomas Piccirello)
- WEBUI: Fix minor JavaScript defects (Thomas Piccirello)
- WEBUI: Add locale to js file path (Thomas Piccirello)
- WEBUI: Translate WebUI torrents Status column (Thomas Piccirello)
- WEBUI: Bump Web API version
- RSS: Allow to disable downloading REPACK/PROPER matches (Stephen Dawkins)
- RSS: Improve RSS Feed updating (glassez)
- SEARCH: Allow resizing search filter in search job (thalieht)
- SEARCH: Improve parser for search engine versions.txt (Chocobo1)
- SEARCH: Update Python URLs (Chocobo1)
- SEARCH: Fix asking to install Python (Chocobo1)
- SEARCH: Reformat python code to be compliant with PEP8 (Chocobo1)
- OTHER: cmake: restore out-of-source build (Eugene Shalygin)
- OTHER: cmake: cmake: use C++14 when available (Eugene Shalygin)
* Tue Sep 18 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.3
- FEATURE: Preselect name without extension when renaming files (thalieht)
- FEATURE: Allow setting seq & first/last from context menu without metadata (thalieht)
- BUGFIX: Show "N/A" if there is no scrape (thalieht)
- BUGFIX: Save option about tracker favicons under correct key (sledgehammer999)
- BUGFIX: When file data are unreachable pause torrent and show "Missing Files" status (temporary fix) (sledgehammer999)
- BUGFIX: Don't disable DHT when using force proxy (Thomas Piccirello)
- BUGFIX: Correctly save torrent queue position/state/priority changes in fastresume (glassez, thalieht, sledgehammer999)
- BUGFIX: Fix icon height/width ratio (Chocobo1)
- BUGFIX: Fix values sorted wrong in "Last Activity" column (Chocobo1)
- BUGFIX: Replace png icons with svg (Chocobo1)
- WEBUI: Allow WebUI sidebar filters to be hidden (Thomas Piccirello)
- WEBUI: Increase WebUI Options initial height (Thomas Piccirello)
- WEBUI: Adjust WebUI Options form alignment (Thomas Piccirello)
- WEBUI: Fix WebUI unreachable issue (Chocobo1)
- WEBUI: Require torrent category creation to be explicit (Thomas Piccirello)
- WEBUI: Include category save path in web api sync data (Thomas Piccirello)
- WEBUI: Add save path and editing to WebUI new category dialog (Thomas Piccirello)
- WEBUI: Bump Web API version
- SEARCH: Refactor in searchjob to always color visited entries (thalieht)
- SEARCH: Set "enter" as shortcut to download the selected torrents in search job (thalieht)
- SEARCH: Add regex option in the search filter's context menu (thalieht)
- LINUX: Fix GUI scaling issue on Linux (Chocobo1)
- LINUX: Fix regression that broke installing desktop file (Eli Schwartz)
- OPENBSD: Better filesystem support for filewatcher (Elias M. Mariani)
* Sun Aug 12 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.2
- FEATURE: New options for "inhibit sleep" (Lukas Greib)
- FEATURE: Add option for regexps in the transferlist search filter's context menu (thalieht)
- FEATURE: Add async io threads option to AdvancedSettings (tjjh89017)
- FEATURE: Allow save resume interval to be disabled (Chocobo1)
- FEATURE: Add checkbox for recursive download dialog (Chocobo1)
- FEATURE: Add changelog link in program updater (Chocobo1)
- BUGFIX: Avoid allocating large memory when loading a .torrent file (Couchy)
- BUGFIX: Notify users on 1st time close/minimize to tray (sledgehammer999)
- BUGFIX: Fix I/O error after fetching magnet metadata (Chocobo1)
- BUGFIX: Never save resume data for already paused torrents (glassez)
- BUGFIX: Make ProgramUpdater upgrade to 64-bit qbt when running on 64-bit Windows (Chocobo1)
- BUGFIX: Put temporary files in qbt own temp folder (Chocobo1)
- BUGFIX: Avoid potentially setting the wrong piece priorities (Chocobo1)
- BUGFIX: Various code refactorings/improvements (Chocobo1, thalieht, glassez)
- BUGFIX: Add options "Download in sequential order" and "Download first and last pieces first" in AddNewTorrentDialog (Chocobo1)
- BUGFIX: Download favicon using appropriate protocol (glassez)
- BUGFIX: Apply proxy settings on DownloadManager creation (glassez)
- BUGFIX: Improve torrent initialization (glassez)
- BUGFIX: Save resume data on torrent change events (glassez)
- BUGFIX: Increase default resume data save interval (Chocobo1)
- BUGFIX: Work around crash when procesing recursive download. Closes #9086 (Chocobo1)
- BUGFIX: Reduce queries to python version (Chocobo1)
- BUGFIX: Disable certain mouse wheel events in Options dialog (Chocobo1)
- WEBUI: Send all rechecks in one request (Thomas Piccirello)
- WEBUI: Add WebUI Force Reannounce option (Thomas Piccirello)
- WEBUI: Create non-existing path in setLocationAction() (Goshik)
- WEBUI: Add WebUI support for Mac ⌘ (Command) key (Thomas Piccirello)
- WEBUI: Show current save path in 'Set location' window (Goshik)
- WEBUI: Fix WebUI cache behavior for css files (Chocobo1)
- WEBUI: Send Cache-Control header in WebUI responses (Chocobo1)
- WEBUI: Add form-action to CSP (Thomas Piccirello)
- WEBUI: Add upgrade-insecure-requests to CSP when HTTPS is enabled (Thomas Piccirello)
- WEBUI: Reset WebUI ban counter on login success (Chocobo1)
- WEBUI: Add logging messages in WebUI login action (Chocobo1)
- WEBUI: Add option to control CSRF protection (Chocobo1)
- WEBUI: Add option to control WebUI clickjacking protection (Chocobo1)
- RSS: Implement "Sequential downloading" feature. Closes #6835 (glassez)
- RSS: Don't use RSS feed URLs as base for file names. Closes #8399 (glassez)
- SEARCH: Add a name filter for search results (thalieht)
- SEARCH: Fix python version detection (Chocobo1)
- SEARCH: Clear python cache conditionally (Chocobo1)
- SEARCH: Properly normalize version string before parsing it (hannsen)
- WINDOWS: Turn on Control Flow Guard for MSVC builds (Chocobo1)
- MACOS: Replace deprecated function IOPMAssertionCreate() on macOS (Chocobo1)
- OTHER: Fix CMake build with QtSingleApplication. Fixes #9196 (Eugene Shalygin)
* Sun May 27 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.1
- FEATURE: Add 'Moving' state for torrents being relocated/moved (sledgehammer999)
- FEATURE: Show rechecking progress (sledgehammer999)
- FEATURE: Add option to remember last used save path (glassez)
- FEATURE: Torrent name is also renamed if the content was renamed in the "Add New Torrent" dialog (glassez)
- FEATURE: Relax behavior of "Download first and last piece first". It applies to all files and not only to the previewable. (Chocobo1)
- BUGFIX: Fix issues with translatable strings (Chocobo1)
- BUGFIX: Fix displayed tracker messages (Chocobo1)
- BUGFIX: Make settings file recovery more robust (Chocobo1)
- BUGFIX: Retry saving settings when operation failed (Chocobo1)
- BUGFIX: Log successful torrent move (sledgehammer999)
- BUGFIX: Fix deletion of old logs (sledgehammer999)
- BUGFIX: Delete non-commited fastresume files (sledgehammer999)
- BUGFIX: Don't migrate torrents that have newer fastresumes (sledgehammer999)
- BUGFIX: Fix adding multiple torrents at once from WebUI (glassez)
- BUGFIX: Improve "Run External Program" behavior. On Windows, a backslash isn't appended to paths from path variables (Chocobo1)
- BUGFIX: Suppress multiple I/O errors for the same torrent (sledgehammer999)
- BUGFIX: Replace raster qbt logo with vector version (Chocobo1)
- WEBUI: Fix wrong API method names (glassez)
- WEBUI: Filter torrent info endpoint by hashes (Marcel Petersen)
- WEBUI: Fix invalid API calls in WebUI (glassez)
- WEBUI: Improve legacy API params handling (glassez)
- WEBUI: Fix params handling for some legacy API methods (glassez)
- WEBUI: Apply locale changes immediately in WebUI (Chocobo1)
- WEBUI: Use 32px icons for favicon (Chocobo1)
- WEBUI/RSS: Properly set RSS settings via API (glassez)
- RSS: Fix auto-downloading rule when Smart filter with regular Episode filter are used (glassez)
- RSS: Make "Ignoring days" to behave like other filters (glassez)
- RSS: Place "Use Smart Episode Filter" more correctly (glassez)
- RSS: Use RSS feed update time as a fallback (glassez)
- COSMETIC: Fix Stats dialog size (sledgehammer999)
- MACOS: Fix GUI scaling factor on macOS (Chocobo1)
- WINDOWS: Update icons (adem4ik)
- LINUX: Fix open destination folder with Nautilus > 3.28 (Evgeny Lensky)
- OTHER: Code improvements and refactoring (thalieht, Nick Korotysh, Chocobo1)
* Sat May 05 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.0
- FEATURE: Add "Coalesce reads & writes" checkbox in advanced options (Chocobo1)
- FEATURE: Smart Filter for RSS (Stephen Dawkins)
- FEATURE: Possibility to configure at which speed a torrent is considered slow (thalieht)
- FEATURE: When creating a torrent you can choose to preserve the file order (toster, Chocobo1)
- FEATURE: A new, redesigned and refactored WebAPI (glassez)
- BUGFIX: Redefine CacheStatus.readRatio field. (Chocobo1)
- BUGFIX: Clarify some terms in stats dialog (Chocobo1)
- BUGFIX: Fix possible crash when using both share limits (thalieht)
- BUGFIX: Disable options when `Disable connections not supported by proxies` is enabled (Thomas Piccirello)
- BUGFIX: Add link to an explanation of `Disable connections not supported by proxies` (Thomas Piccirello)
- BUGFIX: Fix typo in a log message (Andrei Stepanov)
- BUGFIX: Fix loading very large torrents. Closes #8449. (Chocobo1)
- BUGFIX: Fix reverting backslashes to slashes in run external program. Closes #7800 (Chocobo1)
- BUGFIX: Use https for documentation links (Chocobo1)
- BUGFIX: Use original scheme when downloading favicons (Chocobo1)
- BUGFIX: Parse URL query string at application level (glassez)
- BUGFIX: Properly reply to announce request (embedded tracker) (glassez)
- BUGFIX: Add `Tags` parameter to "Run External Program" (Chocobo1)
- BUGFIX: Fix various typos (Chocobo1)
- BUGFIX: Refactor filesystem watcher. Delay before processing new files. (Chocobo1)
- BUGFIX: Don't strip empty arguments passed to external program. Closes #8454. (Chocobo1)
- BUGFIX: Stop creating Download folder on start (Chocobo1)
- BUGFIX: Avoid data corruption when rechecking paused torrents (sledgehammer999)
- BUGFIX: Fix crashes due to invalid iterator use (Luís Pereira)
- BUGFIX: Fix renaming completed files (Chocobo1)
- BUGFIX: Fix path separator in log messages (Chocobo1)
- WEBUI: Switch built-in Web UI html to HTML5 (glassez)
- WEBUI: WebUI Save user's resized window sizes (Thomas Piccirello)
- WEBUI: Make download + upload windows resizable (Thomas Piccirello)
- WEBUI: Add option to show/hide webui status bar (Thomas Piccirello)
- WEBUI: Add "Use proxy only for torrents" option to webui (Thomas Piccirello)
- WEBUI: Various fixes in the html code (Thomas Piccirello)
- WEBUI: Don't unselect selected torrents after a few seconds (Thomas Piccirello)
- WEBUI: Enable Http/1.1 persistence connection (Chocobo1)
- WEBUI: Format Read cache hits as percentage (Thomas Piccirello)
- WEBUI: Re-order and rename stats (Thomas Piccirello)
- WEBUI: Right align stat values (Thomas Piccirello)
- WEBUI: Enable Statistics window to be scrolled and resized (Tom Piccirello)
- WEBUI: Save WebUI Statistics window size (Thomas Piccirello)
- WEBUI: Make WebUI iframe windows scrollable on iOS (Thomas Piccirello)
- WEBUI: Remove unused CSS from WebUI login page (Thomas Piccirello)
- WEBUI: Consolidate CSS into style.css (Thomas Piccirello)
- WEBUI: Resolve JavaScript errors (Thomas Piccirello)
- WEBUI: Fix spacing in login form(Thomas Piccirello)
- WEBUI: Update WebUI to be more compliant with HTML5 standard (Chocobo1)
- WEBUI: Update clipboard.js to v2.0.0 (Chocobo1)
- WEBUI: Remove unused JavaScript library (Chocobo1)
- WEBUI: Fix setting preferences via WebAPI (glassez)
- WEBUI: Rename property to match its definition (Thomas Piccirello)
- WEBUI: Add Limit Share Ratio context menu option (Thomas Piccirello)
- RSS: Disable Auto TMM when RSS rule has save path (glassez)
- RSS: Process loaded RSS articles in case of error (glassez)
- RSS: Resolve (X)HTML entities in RSS content (glassez)
- SEARCH: Improve Search handling (glassez)
- SEARCH: Calculate supported categories based on selected plugin (Thomas Piccirello)
- SEARCH: Fix memory leak (Chocobo1)
- COSMETIC: Use spinbox suffix to display rate/time units (thalieht)
- COSMETIC: Avoid showing an empty row in AdvancedSettings (Chocobo1)
- OTHER: Various code optimizations and fixes (Luís Pereira, Chocobo1)
- OTHER: Fix build when using Clang under CMake (Luís Pereira)
- OTHER: Allow to disable Stacktrace support (Nick Korotysh)
- OTHER: Use RNG provided by OS (Chocobo1)
* Fri Feb 16 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.4
- FEATURE: Add source field in Torrent creator. Closes #7965. (Chocobo1)
- FEATURE: Torrent creator: raise maximum piece size to 32 MiB (Chocobo1)
@@ -408,7 +122,7 @@
- BUGFIX: Optimize code for SpeedWidget. (dzmat)
- BUGFIX: Disable processing events when adding torrents(prevents crashes). Closes #7436. (Chocobo1)
- BUGFIX: Open links in browser. Closes #7651. (Chocobo1)
- BUGFIX: Change default settings for tracker/tier announces to mimic μTorrent behavior. (sledgehammer999)
- BUGFIX: Change default settings for tracker/tier announces to mimick μTorrent behavior. (sledgehammer999)
- BUGFIX: Explicitly set UPnP state on start-up. Closes #7338. (Chocobo1)
- BUGFIX: Include/print caught signal in stackdump (Chocobo1)
- COSMETIC: Trackerlist: Set text alignment of columns with numbers to the right (thalieht)
@@ -539,7 +253,7 @@
- FEATURE: Use Ctrl+F to search torrents. Closes #5797. (Tim Delaney)
- FEATURE: Transferlist: add hotkeys for double click and recheck selected torrents (thalieht)
- FEATURE: Add hotkey for execution log tab, Trackerlist, Peerlist etc (thalieht)
- FEATURE: Separate seeds from peers for DHT, PeX and LSD (thalieht)
- FEATURE: Seperate seeds from peers for DHT, PeX and LSD (thalieht)
- BUGFIX: Do not remove added files unconditionally. Closes #6248 (Eugene Shalygin)
- BUGFIX: Ignore mouse wheel events in Advanced Settings. Closes #866. (Chocobo1)
- BUGFIX: Add queue repair code. It should fix missing torrents after restarting. (Eugene Shalygin, nxd4)
@@ -551,7 +265,7 @@
- BUGFIX: TransferListWidget: keep columns width even if they are hidden on qBittorrent startup (unless something goes wrong) (thalieht)
- BUGFIX: fix index overflow for torrents with invalid meta data or empty progress (Falco)
- BUGFIX: Immediately update torrent_status after manipulating super seeding mode. Partially fixes #6072. (sledgehammer999)
- BUGFIX: Use case-insensitive comparison for torrent content window. Closes #6327. (Chocobo1)
- BUGFIX: Use case-insensitive comparsion for torrent content window. Closes #6327. (Chocobo1)
- BUGFIX: Fixed sort order for datetime columns with empty values (closes #2988) (Vladimir Sinenko)
- BUGFIX: Disable proxy in WebUI HTTP server. Closes #6349. (Eugene Shalygin)
- COSMETIC: Use a disabled progressbar's palette for unselected files. (sledgehammer999)
@@ -679,12 +393,12 @@
- FEATURE: Add option to automatically remove .torrent files upon adding (Eugene Shalygin)
- FEATURE: Add option to bind directly to an IP instead of using a network Interface (Sjoerd van der Berg, sledgehammer999)
- FEATURE: Detailed tooltips on the progress and availability bars in the General button of each torrent. (Eugene Shalygin)
- FEATURE: Let user able to specify a filter when choosing an IP filter file (Chocobo1)
- FEATURE: Let user able to specifiy a filter when choosing an IP filter file (Chocobo1)
- FEATURE: Improve usability of "Run External Program". Users can write (platform dependent) shell scripts now. (Chocobo1)
- PERFORMANCE: Optimize drawing in speed graph (Anton Lashkov, Chocobo1)
- BUGFIX: Fix memory leak. (sledgehammer999)
- BUGFIX: Fix resizing bug in "add torrent dialog". Closes #5036. (Chocobo1)
- BUGFIX: Fix qBittorrent doesn't exit immediately when "all downloads are done -> exit" option enabled. (glassez, Chocobo1)
- BUGFIX: Fix qBittorrent doesn't exit immediately when "all donwloads are done -> exit" option enabled. (glassez, Chocobo1)
- BUGFIX: Display the filepath when a torrent fails to load. Closes #100 and #805. (sledgehammer999)
- BUGFIX: Fix Add tracker dialog empty trackers (ngosang)
- BUGFIX: Fix Add tracker dialog URL download (ngosang)
@@ -737,7 +451,7 @@
- OTHER: Enable access to shutdown functions when configured with `--disable-gui` option (Chocobo1)
- OTHER: Delete Import Torrent Dialog. Just use the "add new torrent" dialog. (glassez)
- OTHER: Optimize code for natural sorting (Chocobo1)
- OTHER: Use new alert dispatching API for libtorrent 1.1.x (glassez)
- OTHER: Use new alert dispathing API for libtorrent 1.1.x (glassez)
- OTHER: Fix gcc 6 compilation with qmake. See #5237. (sledgehammer999)
* Tue Mar 29 2016 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.4
@@ -1061,7 +775,7 @@
- SEARCH: Fix thepiratebay. Closes #3012 (ngosang)
- SEARCH: Improve torrentz engine to return more results (ngosang)
- SEARCH: Change width of columns in search tab. Closes #764 (ngosang)
- SEARCH: Make strings translatable in search engine (ngosang)
- SEARCH: Make strings translatable in seach engine (ngosang)
- SEARCH: Aborting search engine process during closure. Close #2671 (DoumanAsh)
- SEARCH: Perform searches in parallel (DoumanAsh)
- SEARCH: Add Demonoid search engine (ngosang)
@@ -1228,7 +942,7 @@
- WEBUI: Removed broken 'Documentation'. Improves fix for #1343 (Benjamin Hutchins)
- WEBUI: Removed essentially useless 'Visit website' iframe and changed it to a regular link. Improves fix for #1343 (Benjamin Hutchins)
- BUGFIX: Fix RSS feed icon. The tmp file gets deleted in the feed destructor. Closes #1639 (sledgehammer999)
- BUGFIX: fix issue #1674: AddNewTorrentDialog is shown again and again even if checkbox "don't ask me again" is set (Ivan Sorokin)
- BUGFIX: fix issue #1674: AddNewTorrentDialog is shown again and again even if checkbox "dont ask me again" is set (Ivan Sorokin)
- BUGFIX: Don't show availability bar for magnet links (Ivan Sorokin)
- BUGFIX: Fix crash when the selected torrent disappears from the transfer list. Closes #1661 (sledgehammer999)
- BUGFIX: Fix tracker announcing problem(hit-and-run) when many torrents are being active. Closes #1571 (sledgehammer999)
@@ -1362,7 +1076,7 @@
- FEATURE: Show external IP in the log. Closes #968. (sledgehammer999)
- FEATURE: Enable gzip compression in the webui. It should be faster now. (sledgehammer999)
- FEATURE: Torrents show more states(queued for checking, downloading metadata, allocating, checking resume). (sledgehammer999)
- FEATURE: Re-enable "force reannounce" to all trackers. (sledgehammer999)
- FEATURE: Reenable "force reannounce" to all trackers. (sledgehammer999)
- FEATURE: Allow to clear the UI lock password. Closes #973. (sledgehammer999)
- FEATURE: New translations: English(Australia) and English(United Kingdom)
- BUGFIX: Expose all available translation in the WebUI. Closes #976. (sledgehammer999)
@@ -1411,8 +1125,8 @@
- OTHER: Make peer tab sortable by ip too (Gelmir)
- OTHER: Translations moved to Transifex(https://www.transifex.com/projects/p/qbittorrent/)
- OTHER: New Translation - Vietnamese (Anh Phan)
- PERFORMANCE: Improve drawing speed of tranferlist when there are many torrents(>100)
- PERFORMANCE: Improve drawing speed of peers list when there are many peers
- PERFORMANCE: Impove drawing speed of tranferlist when there are many torrents(>100)
- PERFORMANCE: Impove drawing speed of peers list when there are many peers
* Mon Jul 29 2013 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.0.11
- FEATURE: Allow more fine tuning of upload slots. It should improve speed (sledgehammer999)
@@ -1452,8 +1166,8 @@
- LIBTORRENT: SOCKS5 fixes (0.16.10)
- LIBTORRENT: Fix hanging issue on Windows when closing files (0.16.10)
- LIBTORRENT: Cache can now be returned to the OS (0.16.10)
- PERFORMANCE: Improve drawing speed of tranferlist when there are many torrents(>100) (sledgehammer999)
- PERFORMANCE: Improve drawing speed of peers list when there are many peers (sledgehammer999)
- PERFORMANCE: Impove drawing speed of tranferlist when there are many torrents(>100) (sledgehammer999)
- PERFORMANCE: Impove drawing speed of peers list when there are many peers (sledgehammer999)
* Sat Mar 16 2013 - Christophe Dumez <chris@qbittorrent.org> - v3.0.9
- BUGFIX: Raise qBittorrent windows when another instance is launched
@@ -1477,7 +1191,7 @@
- BUGFIX: Fix "Couldn't set environment variable..." message on start up (closes #245)
- BUGFIX: Use right path separator in torrent addition dialog on Windows
- BUGFIX: Fix "Set as default save path" setting (closes #254)
- BUGFIX: Re-enable disk cache on Windows since the memory issue seems to be gone
- BUGFIX: Reenable disk cache on Windows since the memory issue seems to be gone
- BUGFIX: Fixed several search engine plugins and removed the dead ones
- BUGFIX: Use https links in search plugins when possible
- BUGFIX: Bump Mootools to v1.4.5 (Web UI)
@@ -1613,7 +1327,7 @@
- I18N: Add Georgian translation
* Sat Oct 29 2011 - Christophe Dumez <chris@qbittorrent.org> - v2.9.2
- BUGFIX: Fix minimum dimensions for torrent addition dialog
- BUGFIX: Fix mimimum dimensions for torrent addition dialog
- BUGFIX: Remove dependency on boost-datetime
- BUGFIX: Remove dependency on boost-filesystem (libtorrent v0.16.x)
@@ -1824,7 +1538,7 @@
- BUGFIX: Update RSS feed as soon as feed downloader is enabled
- BUGFIX: RSS Feed downloader ignores articles above maximum number of articles
- BUGFIX: Fix possible bug when deleting a RSS folder
- BUGFIX: Remove persistent data when a RSS feed is deleted
- BUGFIX: Remove persistant data when a RSS feed is deleted
- BUGFIX: RSS filters are now alphabetically sorted
- BUGFIX: Fix crash when renaming currently displayed RSS filter
- BUGFIX: Remove overwriting confirmation when exporting RSS filters since Qt takes care of it
@@ -1860,7 +1574,7 @@
- BUGFIX: Use the save path set in program preferences as a default in torrent addition dialog
* Fri Dec 18 2009 - Christophe Dumez <chris@qbittorrent.org> - v2.0.2
- BUGFIX: Fix .qbittorrent folder not being created (critical bug introduced in v2.0.1 that makes qBittorrent unusable for new users)
- BUGFIX: Fix .qbittorrent folder not being created (critical bug introduced in v2.0.1 that makes qBittorrent unusuable for new users)
- BUGFIX: Fix RSS Feed downloader for some feeds
- BUGFIX: Do not use home folder as a fallback when the save path is not accessible
- BUGFIX: Fix Mininova, ThePirateBay search engine plugins
@@ -1950,7 +1664,7 @@
- BUGFIX: Fix trackers addition to torrents (bug introduced in v1.5.4)
- BUGFIX: Suppress compilation warning regarding sortNewsList() not being used
- BUGFIX: Make sure scan folder is different than qBittorrent backup directory to avoid torrents deletion
- BUGFIX: Added safety mechanism which adds the torrents back to the list in case qbittorrent-resume.conf gets deleted or corrupted.
- BUGFIX: Added safety mecanism which adds the torrents back to the list in case qbittorrent-resume.conf gets deleted or corrupted.
* Sun Oct 25 2009 - Christophe Dumez <chris@qbittorrent.org> - v1.5.4
- BUGFIX: Updated man page
@@ -2017,7 +1731,7 @@
- FEATURE: Added right click menu in search engine to clear completion history
- FEATURE: Allow to set a different port for DHT (UDP) than the one used for Bittorrent
- FEATURE: Updated spoofing code to avoid trackers ban
- BUGFIX: Provide more helpful explanation when an I/O error occurred
- BUGFIX: Provide more helpful explanation when an I/O error occured
- BUGFIX: Stop enforcing UTF-8 and use system locale instead
- COSMETIC: Redesigned program preferences
- COSMETIC: Updated icons set
@@ -2035,7 +1749,7 @@
- BUGFIX: Suppressed QLayout: Attempting to add QLayout "" to properties "properties" warning message when opening a properties dialog
- BUGFIX: Fixed a little bug in search engine plugins helper file
- BUGFIX: Fixed compilation problems with Qt 4.3
- BUGFIX: Percentages no longer disappear with default cleanlooks style
- BUGFIX: Percentages no longer disapear with default cleanlooks style
- BUGFIX: Cleanly fixed popup menus position in lists (no more workarounds)
- BUGFIX: Fixed memory leak in search engine
- BUGFIX: Torrents with an infinite ratio are no longer affected by ratio_limit set in program preferences
@@ -2280,7 +1994,7 @@
- FEATURE: Number of complete/incomplete sources are now displayed in download list for each torrent
- FEATURE: Implemented close to systray
- FEATURE: Added Autocompletion to search engine
- FEATURE: Split BT & GUI parts (huge code rewriting & optimization)
- FEATURE: Splitted BT & GUI parts (huge code rewriting & optimization)
- FEATURE: New parameters for configure file to point to custom locations for libtorrent/libcurl
- FEATURE: Update application style according to the system (WindowsXP, MacOS, X11)
- BUGFIX: Two torrents can now have the same name although they are different (use their hash)

49
INSTALL
View File

@@ -1,49 +1,54 @@
qBittorrent - A BitTorrent client in C++ / Qt
qBittorrent - A BitTorrent client in C++ / Qt4
------------------------------------------
1) Compile and install qBittorrent with Qt graphical interface
1) Compile and install qBittorrent with Qt4 Graphical Interface
$ ./configure
$ make && make install
$ qbittorrent
will install and execute qBittorrent.
will install and execute qBittorrent hopefully without any problems.
Dependencies:
- Qt >= 5.5.1
- Qt >= 4.6.0 (libqtgui, libqtcore, libqtnetwork, libqtxml, libqtdbus/optional)
- pkg-config
- pkg-config executable
- libtorrent-rasterbar >= 1.0.6 (by Arvid Norberg)
* https://www.libtorrent.org/
* Be careful: another library (the one used by rTorrent) uses a similar name
- libtorrent-rasterbar by Arvid Norberg (>= 1.0.6)
-> http://www.libtorrent.net
Be careful: another library (the one used by rTorrent) uses a similar name.
- Boost >= 1.35
- libboost >= 1.35.x (libboost-system)
- Python >= 2.7.9 / 3.3.0 (optional, runtime only)
* Required by the internal search engine
- python >= 2.3 (needed by search engine)
* Run time only dependency
2) Compile and install qBittorrent without Qt graphical interface
- geoip-database (optional)
* If qBittorrent cannot find this database, it will try to resolve countries using the Internet but it will be a lot slower.
* Run time only dependency
2) Compile and install qBittorrent without Qt4 Graphical interface
$ ./configure --disable-gui
$ make && make install
$ qbittorrent-nox
$ qbittorrent
will install and execute qBittorrent.
will install and execute qBittorrent hopefully without any problems.
Dependencies:
- Qt >= 5.5.1
- Qt >= 4.4.0 (libqt-devel, libqtcore, libqtnetwork)
- pkg-config
- pkg-config executable
- libtorrent-rasterbar >= 1.0.6 (by Arvid Norberg)
* https://www.libtorrent.org/
* Be careful: another library (the one used by rTorrent) uses a similar name
- libtorrent-rasterbar by Arvid Norberg (>= v1.0.6)
-> http://www.libtorrent.net
Be careful: another library (the one used by rTorrent) uses a similar name.
- libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization
- Boost >= 1.35
DOCUMENTATION:
Please note that there is a "Compilation" section at http://wiki.qbittorrent.org.
Please note that there is a documentation with a "compiling howto" at http://wiki.qbittorrent.org.
------------------------------------------
sledgehammer999 <sledgehammer999@qbittorrent.org>
Christophe Dumez <chris@qbittorrent.org>

View File

@@ -1,22 +0,0 @@
# Helper function for coupling add_feature_info(), option(), and add_definitions()
function(optional_compile_definitions _name)
set(options FEATURE)
set(oneValueArgs DESCRIPTION DEFAULT)
set(multiValueArgs ENABLED DISABLED)
cmake_parse_arguments(OCD "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
option(${_name} "${OCD_DESCRIPTION}" ${OCD_DEFAULT})
if (${${_name}})
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY COMPILE_DEFINITIONS ${OCD_ENABLED})
else()
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY COMPILE_DEFINITIONS ${OCD_DISABLED})
endif()
if(${OCD_FEATURE})
add_feature_info(${_name} ${_name} "${OCD_DESCRIPTION}")
endif()
endfunction()
macro(feature_option _name _description _default)
option(${_name} "${_description}" ${_default})
add_feature_info(${_name} ${_name} "${_description}")
endmacro()

View File

@@ -99,7 +99,6 @@ list(FIND LibtorrentRasterbar_DEFINITIONS -DTORRENT_USE_OPENSSL LibtorrentRaster
if(LibtorrentRasterbar_ENCRYPTION_INDEX GREATER -1)
find_package(OpenSSL REQUIRED)
set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto)
list(APPEND LibtorrentRasterbar_INCLUDE_DIRS "${OPENSSL_INCLUDE_DIR}")
set(LibtorrentRasterbar_OPENSSL_ENABLED ON)
endif()
@@ -114,10 +113,10 @@ mark_as_advanced(LibtorrentRasterbar_INCLUDE_DIR LibtorrentRasterbar_LIBRARY
LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES
LibtorrentRasterbar_ENCRYPTION_INDEX)
if (LibtorrentRasterbar_FOUND AND NOT TARGET LibtorrentRasterbar::torrent-rasterbar)
add_library(LibtorrentRasterbar::torrent-rasterbar UNKNOWN IMPORTED)
if (LibtorrentRasterbar_FOUND AND NOT TARGET LibtorrentRasterbar::LibTorrent)
add_library(LibtorrentRasterbar::LibTorrent UNKNOWN IMPORTED)
set_target_properties(LibtorrentRasterbar::torrent-rasterbar PROPERTIES
set_target_properties(LibtorrentRasterbar::LibTorrent PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${LibtorrentRasterbar_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}"

View File

@@ -1,79 +1,94 @@
# - Try to find the QtSingleApplication includes and library
# which defines
#
# QtSingleApplication_FOUND - system has QtSingleApplication
# QtSingleApplication_INCLUDE_DIR - where to find header QtSingleApplication
# QtSingleApplication_LIBRARIES - the libraries to link against to use QtSingleApplication
# QtSingleApplication_LIBRARY - where to find the QtSingleApplication library (not for general use)
# QTSINGLEAPPLICATION_FOUND - system has QtSingleApplication
# QTSINGLEAPPLICATION_INCLUDE_DIR - where to find header QtSingleApplication
# QTSINGLEAPPLICATION_LIBRARIES - the libraries to link against to use QtSingleApplication
# QTSINGLEAPPLICATION_LIBRARY - where to find the QtSingleApplication library (not for general use)
# copyright (c) 2013 TI_Eugene ti.eugene@gmail.com
#
# Redistribution and use is allowed according to the terms of the FreeBSD license.
SET(QtSingleApplication_FOUND FALSE)
SET(QTSINGLEAPPLICATION_FOUND FALSE)
if (Qt5Widgets_FOUND)
set(_includeFileName qtsingleapplication.h)
else()
set(_includeFileName qtsinglecoreapplication.h)
endif()
IF(QT4_FOUND)
message(STATUS "Looking for Qt4 single application library")
FIND_PATH(QTSINGLEAPPLICATION_INCLUDE_DIR QtSingleApplication
# standard locations
/usr/include
/usr/include/QtSolutions
# qt4 location except mac's frameworks
"${QT_INCLUDE_DIR}/QtSolutions"
# mac's frameworks
${FRAMEWORK_INCLUDE_DIR}/QtSolutions
)
FOREACH(TOP_INCLUDE_PATH in ${Qt5Core_INCLUDE_DIRS} ${FRAMEWORK_INCLUDE_DIR})
FIND_PATH(QtSingleApplication_INCLUDE_DIR ${_includeFileName} ${TOP_INCLUDE_PATH}/QtSolutions)
SET(QTSINGLEAPPLICATION_NAMES ${QTSINGLEAPPLICATION_NAMES}
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
FIND_LIBRARY(QTSINGLEAPPLICATION_LIBRARY
NAMES ${QTSINGLEAPPLICATION_NAMES}
PATHS ${QT_LIBRARY_DIR}
)
ELSEIF(Qt5Core_FOUND)
message(STATUS "Looking for Qt5 single application library")
FOREACH(TOP_INCLUDE_PATH in ${Qt5Core_INCLUDE_DIRS} ${FRAMEWORK_INCLUDE_DIR})
FIND_PATH(QTSINGLEAPPLICATION_INCLUDE_DIR QtSingleApplication ${TOP_INCLUDE_PATH}/QtSolutions)
IF(QtSingleApplication_INCLUDE_DIR)
BREAK()
ENDIF()
ENDFOREACH()
IF(QTSINGLEAPPLICATION_INCLUDE_DIR)
BREAK()
ENDIF()
ENDFOREACH()
SET(QtSingleApplication_NAMES ${QtSingleApplication_NAMES}
Qt5Solutions_SingleApplication-2.6 libQt5Solutions_SingleApplication-2.6
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
GET_TARGET_PROPERTY(_QT5_CORELIBRARY Qt5::Core LOCATION)
GET_FILENAME_COMPONENT(_QT5_CORELIBRARYPATH ${_QT5_CORELIBRARY} PATH)
SET(QTSINGLEAPPLICATION_NAMES ${QTSINGLEAPPLICATION_NAMES}
Qt5Solutions_SingleApplication-2.6 libQt5Solutions_SingleApplication-2.6
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
GET_TARGET_PROPERTY(_QT5_CORELIBRARY Qt5::Core LOCATION)
GET_FILENAME_COMPONENT(_QT5_CORELIBRARYPATH ${_QT5_CORELIBRARY} PATH)
FIND_LIBRARY(QtSingleApplication_LIBRARY
NAMES ${QtSingleApplication_NAMES}
PATHS ${_QT5_CORELIBRARYPATH}
)
FIND_LIBRARY(QTSINGLEAPPLICATION_LIBRARY
NAMES ${QTSINGLEAPPLICATION_NAMES}
PATHS ${_QT5_CORELIBRARYPATH}
)
ENDIF()
IF (QtSingleApplication_LIBRARY AND QtSingleApplication_INCLUDE_DIR)
IF (QTSINGLEAPPLICATION_LIBRARY AND QTSINGLEAPPLICATION_INCLUDE_DIR)
SET(QtSingleApplication_LIBRARIES ${QtSingleApplication_LIBRARY})
SET(QtSingleApplication_FOUND TRUE)
SET(QTSINGLEAPPLICATION_LIBRARIES ${QTSINGLEAPPLICATION_LIBRARY})
SET(QTSINGLEAPPLICATION_FOUND TRUE)
IF (CYGWIN)
IF(BUILD_SHARED_LIBS)
# No need to define QtSingleApplication_USE_DLL here, because it's default for Cygwin.
# No need to define QTSINGLEAPPLICATION_USE_DLL here, because it's default for Cygwin.
ELSE(BUILD_SHARED_LIBS)
SET (QtSingleApplication_DEFINITIONS -DQTSINGLEAPPLICATION_STATIC)
SET (QTSINGLEAPPLICATION_DEFINITIONS -DQTSINGLEAPPLICATION_STATIC)
ENDIF(BUILD_SHARED_LIBS)
ENDIF (CYGWIN)
ENDIF (QtSingleApplication_LIBRARY AND QtSingleApplication_INCLUDE_DIR)
ENDIF (QTSINGLEAPPLICATION_LIBRARY AND QTSINGLEAPPLICATION_INCLUDE_DIR)
IF (QtSingleApplication_FOUND)
IF (NOT QtSingleApplication_FIND_QUIETLY)
MESSAGE(STATUS "Found QtSingleApplication: ${QtSingleApplication_LIBRARY}")
MESSAGE(STATUS " includes: ${QtSingleApplication_INCLUDE_DIR}")
ENDIF (NOT QtSingleApplication_FIND_QUIETLY)
if(NOT TARGET QtSingleApplication::QtSingleApplication)
add_library(QtSingleApplication::QtSingleApplication UNKNOWN IMPORTED)
set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${QtSingleApplication_INCLUDE_DIR}"
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${QtSingleApplication_INCLUDE_DIR}"
)
if(EXISTS "${QtSingleApplication_LIBRARY}")
set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${QtSingleApplication_LIBRARY}")
endif()
endif(NOT TARGET QtSingleApplication::QtSingleApplication)
ELSE (QtSingleApplication_FOUND)
IF (QTSINGLEAPPLICATION_FOUND)
IF (NOT QtSingleApplication_FIND_QUIETLY)
MESSAGE(STATUS "Found QtSingleApplication: ${QTSINGLEAPPLICATION_LIBRARY}")
MESSAGE(STATUS " includes: ${QTSINGLEAPPLICATION_INCLUDE_DIR}")
ENDIF (NOT QtSingleApplication_FIND_QUIETLY)
ELSE (QTSINGLEAPPLICATION_FOUND)
IF (QtSingleApplication_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find QtSingleApplication library")
ENDIF (QtSingleApplication_FIND_REQUIRED)
ENDIF (QtSingleApplication_FOUND)
ENDIF (QTSINGLEAPPLICATION_FOUND)
MARK_AS_ADVANCED(QtSingleApplication_INCLUDE_DIR QtSingleApplication_LIBRARY)
MARK_AS_ADVANCED(QTSINGLEAPPLICATION_INCLUDE_DIR QTSINGLEAPPLICATION_LIBRARY)
if(NOT TARGET QtSingleApplication::QtSingleApplication)
add_library(QtSingleApplication::QtSingleApplication UNKNOWN IMPORTED)
set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${QTSINGLEAPPLICATION_INCLUDE_DIR}"
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${QTSINGLEAPPLICATION_INCLUDE_DIR}"
)
if(EXISTS "${QTSINGLEAPPLICATION_LIBRARY}")
set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
IMPORTED_LOCATION "${QTSINGLEAPPLICATION_LIBRARY}")
endif()
endif(NOT TARGET QtSingleApplication::QtSingleApplication)

View File

@@ -1,5 +1,5 @@
# - macro similar to target_link_libraries, which links Qt components
# names of the components are passed in Qt4/Qt5 agnostic way (Core, DBus, Xml...)
# names of the components are pased in Qt4/Qt5 agnostic way (Core, DBus, Xml...)
# and the macro links Qt4 ones if QT4_FOUND is set or Qt5 ones if not
macro (target_link_qt_components target)

View File

@@ -11,17 +11,18 @@ macro(qbt_set_compiler_options)
#-Wshadow -Wconversion ?
set(_GCC_COMMON_C_AND_CXX_FLAGS "-Wall -Wextra"
"-Wfloat-equal -Wcast-qual -Wcast-align"
"-Wsign-conversion -Winvalid-pch -Wno-long-long"
#"-fstack-protector-all"
#"-Werror -Wno-error=deprecated-declarations"
"-Wsign-conversion -Winvalid-pch -Werror=return-type -Wno-long-long"
# -fstack-protector-all
"-Werror -Wno-error=deprecated-declarations"
)
set(_GCC_COMMON_CXX_FLAGS "-fexceptions -frtti"
"-Woverloaded-virtual -Wold-style-cast"
set (_GCC_COMMON_CXX_FLAGS "-fexceptions -frtti"
"-Woverloaded-virtual -Wold-style-cast -Wstrict-null-sentinel"
"-Wnon-virtual-dtor -Wfloat-equal -Wcast-qual -Wcast-align"
#"-Weffc++"
#"-Werror -Wno-error=cpp"
"-Werror=overloaded-virtual"
# "-Weffc++"
"-Werror -Wno-error=cpp"
# we should modify code to make these ones obsolete
#"-Wno-error=sign-conversion -Wno-error=float-equal"
"-Wno-error=sign-conversion -Wno-error=float-equal"
)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
@@ -53,20 +54,6 @@ macro(qbt_set_compiler_options)
endif(${GLIBC_VERSION})
endif (CMAKE_SYSTEM_NAME MATCHES Linux)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# Clang 5.0 still doesn't support -Wstrict-null-sentinel
check_cxx_compiler_flag(-Wstrict-null-sentinel _STRICT_NULL_SENTINEL_IS_SUPPORTED)
if (_STRICT_NULL_SENTINEL_IS_SUPPORTED)
list(APPEND _GCC_COMMON_CXX_FLAGS "-Wstrict-null-sentinel")
endif (_STRICT_NULL_SENTINEL_IS_SUPPORTED)
# Code should be improved to render this not needed
list(APPEND _GCC_COMMON_CXX_FLAGS "-Wno-error=unused-function -Wno-error=inconsistent-missing-override")
else ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# GCC supports it
list(APPEND _GCC_COMMON_CXX_FLAGS "-Wstrict-null-sentinel")
endif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
string(REPLACE ";" " " _GCC_COMMON_C_AND_CXX_FLAGS_STRING "${_GCC_COMMON_C_AND_CXX_FLAGS}")
string(REPLACE ";" " " _GCC_COMMON_CXX_FLAGS_STRING "${_GCC_COMMON_CXX_FLAGS}")

View File

@@ -1,17 +1,16 @@
# a helper function which appends source to the target
# sources file names are relative to the the target source dir
# a helper function which appends source to the main qBt target
# sources file names are relative to the the ${qBittorrent_SOURCE_DIR}
function (qbt_target_sources _target _scope)
get_target_property(targetSourceDir ${_target} SOURCE_DIR)
set(sourcesRelative "")
foreach(source IN ITEMS ${ARGN})
if(IS_ABSOLUTE "${source}")
set(sourceAbsolutePath "${source}")
function (qbt_target_sources)
set (_sources_rel "")
foreach (_source IN ITEMS ${ARGN})
if (IS_ABSOLUTE "${_source}")
set(source_abs "${_source}")
else()
get_filename_component(sourceAbsolutePath "${source}" ABSOLUTE)
get_filename_component(_source_abs "${_source}" ABSOLUTE)
endif()
file(RELATIVE_PATH sourceRelativePath "${targetSourceDir}" "${sourceAbsolutePath}")
list(APPEND sourcesRelative "${sourceRelativePath}")
file (RELATIVE_PATH _source_rel "${qbt_executable_SOURCE_DIR}" "${_source_abs}")
list (APPEND _sources_rel "${_source_rel}")
endforeach()
target_sources(${_target} ${_scope} "${sourcesRelative}")
endfunction(qbt_target_sources)
target_sources (qBittorrent PRIVATE "${_sources_rel}")
endfunction (qbt_target_sources)

View File

@@ -1,48 +0,0 @@
# macros to handle translation files
# qbt_add_translations(<target> QRC_FILE <filename> TS_FILES <filenames>)
# handles out of source builds for Qt resource files that include translations
# The function generates translations out of the supplied list of .ts files in the build directory,
# copies the .qrc file there, calls qt5_add_resources() adds its output to the target sources list.
function(qbt_add_translations _target)
set(oneValueArgs QRC_FILE)
set(multiValueArgs TS_FILES)
cmake_parse_arguments(QBT_TR "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
get_target_property(_binaryDir ${_target} BINARY_DIR)
if (NOT QBT_TR_QRC_FILE)
message(FATAL_ERROR "QRC file is empty")
endif()
if (NOT QBT_TR_TS_FILES)
message(FATAL_ERROR "TS_FILES files are empty")
endif()
if(IS_ABSOLUTE "${QBT_TR_QRC_FILE}")
file(RELATIVE_PATH _qrcToTs "${CMAKE_CURRENT_SOURCE_DIR}" "${QBT_TR_QRC_FILE}")
else()
set(_qrcToTs "${QBT_TR_QRC_FILE}")
endif()
get_filename_component(_qrcToTsDir "${_qrcToTs}" DIRECTORY)
get_filename_component(_qmFilesBinaryDir "${CMAKE_CURRENT_BINARY_DIR}/${_qrcToTsDir}" ABSOLUTE)
# to make qt5_add_translation() work as we need
set_source_files_properties(${QBT_TR_TS_FILES} PROPERTIES OUTPUT_LOCATION "${_qmFilesBinaryDir}")
qt5_add_translation(_qmFiles ${QBT_TR_TS_FILES})
set(_qrc_dest_dir "${_binaryDir}/${_qrcToTsDir}")
set(_qrc_dest_file "${_binaryDir}/${QBT_TR_QRC_FILE}")
message(STATUS "copying ${QBT_TR_QRC_FILE} to ${_qrc_dest_dir}")
file(COPY ${QBT_TR_QRC_FILE} DESTINATION ${_qrc_dest_dir})
set_source_files_properties("${_qrc_dest_file}" PROPERTIES
GENERATED True
OBJECT_DEPENDS "${_qmFiles}")
# With AUTORCC enabled rcc is ran by cmake before language files are generated,
# and thus we call rcc explicitly
qt5_add_resources(_resources "${_qrc_dest_file}")
target_sources(${_target} PRIVATE "${_resources}")
endfunction()

View File

@@ -1,9 +1,9 @@
if (STACKTRACE)
if (STACKTRACE_WIN)
if ("${WINXXBITS}" NOT STREQUAL "Win64")
add_compile_options(-fno-omit-frame-pointer)
endif ("${WINXXBITS}" NOT STREQUAL "Win64")
link_libraries(libdbghelp -Wl,--export-all-symbols)
endif (STACKTRACE)
endif (STACKTRACE_WIN)
if (("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") OR ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"))
link_libraries(-Wl,--dynamicbase)

View File

@@ -1,4 +1,4 @@
if (STACKTRACE)
if (STACKTRACE_WIN)
if ("${WINXXBITS}" STREQUAL "Win64")
add_compile_options(-Zi)
else ("${WINXXBITS}" STREQUAL "Win64")
@@ -6,7 +6,7 @@ if (STACKTRACE)
add_compile_options(-Oy-)
endif ("${WINXXBITS}" STREQUAL "Win64")
link_libraries(dbghelp.lib)
endif (STACKTRACE)
endif (STACKTRACE_WIN)
# Enable Wide characters
add_definitions(-DTORRENT_USE_WPATH)

View File

@@ -5,8 +5,6 @@ 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@

View File

@@ -50,8 +50,6 @@ DEFINES += BOOST_USE_WINAPI_VERSION=0x0501
#DEFINES += BOOST_ASIO_SEPARATE_COMPILATION
# Enable if building against libtorrent 1.0.x (RC_1_0) (dynamic linking)
#DEFINES += BOOST_ASIO_DYN_LINK
# Enable if encountered build error with boost version <= 1.59
#DEFINES += BOOST_NO_CXX11_RVALUE_REFERENCES
# Enable if building against libtorrent 1.1.x (RC_1_1)
# built with this flag defined
@@ -60,10 +58,4 @@ DEFINES += BOOST_USE_WINAPI_VERSION=0x0501
#DEFINES += TORRENT_LINKING_SHARED
# Enable stack trace support
CONFIG += stacktrace
win32-msvc* {
QMAKE_CXXFLAGS += "/guard:cf"
QMAKE_LFLAGS += "/guard:cf"
QMAKE_LFLAGS_RELEASE += "/OPT:REF /OPT:ICF"
}
CONFIG += strace_win

530
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.6.
# Generated by GNU Autoconf 2.69 for qbittorrent v4.0.4.
#
# 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.6'
PACKAGE_STRING='qbittorrent v4.1.6'
PACKAGE_VERSION='v4.0.4'
PACKAGE_STRING='qbittorrent v4.0.4'
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
PACKAGE_URL='https://www.qbittorrent.org/'
@@ -595,8 +595,6 @@ QBT_REMOVE_CONFIG
QBT_ADD_CONFIG
QBT_CONF_EXTRA_CFLAGS
QBT_CONF_INCLUDES
QBT_CXX
QBT_CC
EXPAND_MANDIR
EXPAND_DATADIR
EXPAND_BINDIR
@@ -628,6 +626,7 @@ am__nodep
AMDEPBACKSLASH
AMDEP_FALSE
AMDEP_TRUE
am__quote
am__include
DEPDIR
am__untar
@@ -710,8 +709,7 @@ PACKAGE_VERSION
PACKAGE_TARNAME
PACKAGE_NAME
PATH_SEPARATOR
SHELL
am__quote'
SHELL'
ac_subst_files=''
ac_user_opts='
enable_option_checking
@@ -719,7 +717,6 @@ enable_dependency_tracking
enable_silent_rules
with_qtsingleapplication
enable_debug
enable_stacktrace
enable_gui
enable_systemd
enable_webui
@@ -1299,7 +1296,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures qbittorrent v4.1.6 to adapt to many kinds of systems.
\`configure' configures qbittorrent v4.0.4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1370,7 +1367,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of qbittorrent v4.1.6:";;
short | recursive ) echo "Configuration of qbittorrent v4.0.4:";;
esac
cat <<\_ACEOF
@@ -1385,7 +1382,6 @@ Optional Features:
--enable-silent-rules less verbose build output (undo: "make V=1")
--disable-silent-rules verbose build output (undo: "make V=0")
--enable-debug Enable debug build
--enable-stacktrace Enable stacktrace feature (default=auto)
--disable-gui Disable the GUI for headless running. Disables
QtDBus and the GeoIP Database.
--enable-systemd Install the systemd service file (headless only).
@@ -1505,7 +1501,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
qbittorrent configure v4.1.6
qbittorrent configure v4.0.4
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1644,7 +1640,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by qbittorrent $as_me v4.1.6, which was
It was created by qbittorrent $as_me v4.0.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -3276,7 +3272,7 @@ IFS=$ac_save_IFS
case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
am__api_version='1.16'
am__api_version='1.15'
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
@@ -3702,45 +3698,45 @@ DEPDIR="${am__leading_dot}deps"
ac_config_commands="$ac_config_commands depfiles"
{ $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_make=${MAKE-make}
cat > confinc << 'END'
am__doit:
@echo this is the am__doit target >confinc.out
@echo this is the am__doit target
.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=
# 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 ;; #(
*) :
;;
_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
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; }
# 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
;;
esac
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
$as_echo "$_am_result" >&6; }
rm -f confinc confmf
# Check whether --enable-dependency-tracking was given.
if test "${enable_dependency_tracking+set}" = set; then :
@@ -3822,7 +3818,7 @@ fi
# Define the identity of the package.
PACKAGE='qbittorrent'
VERSION='v4.1.6'
VERSION='v4.0.4'
cat >>confdefs.h <<_ACEOF
@@ -3852,8 +3848,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:
# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
# <http://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
@@ -4160,7 +4156,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: <https://www.gnu.org/software/coreutils/>.
that behaves properly: <http://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
@@ -4172,9 +4168,7 @@ END
fi
# use compiler from env variables if available
QBT_CC="$CC"
QBT_CXX="$CXX"
# Define --wth-* and --enable-* arguments
@@ -4195,14 +4189,6 @@ else
fi
# Check whether --enable-stacktrace was given.
if test "${enable_stacktrace+set}" = set; then :
enableval=$enable_stacktrace;
else
enable_stacktrace=auto
fi
# Check whether --enable-gui was given.
if test "${enable_gui+set}" = set; then :
enableval=$enable_gui;
@@ -4403,39 +4389,6 @@ $as_echo "$enable_debug" >&6; }
as_fn_error $? "Unknown option \"$enable_debug\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the stacktrace feature" >&5
$as_echo_n "checking whether to enable the stacktrace feature... " >&6; }
case "x$enable_stacktrace" in #(
"xno") :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG stacktrace" ;; #(
"xyes") :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
QBT_ADD_CONFIG="$QBT_ADD_CONFIG stacktrace" ;; #(
"xauto") :
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <execinfo.h>
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
QBT_ADD_CONFIG="$QBT_ADD_CONFIG stacktrace"
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG stacktrace"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ;; #(
*) :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_stacktrace" >&5
$as_echo "$enable_stacktrace" >&6; }
as_fn_error $? "Unknown option \"$enable_stacktrace\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the GUI" >&5
$as_echo_n "checking whether to enable the GUI... " >&6; }
case "x$enable_gui" in #(
@@ -4683,6 +4636,7 @@ esac
# Check whether --with-boost was given.
if test "${with_boost+set}" = set; then :
withval=$with_boost;
@@ -5001,10 +4955,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 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;}
{ $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;}
fi
CPPFLAGS="$CPPFLAGS_SAVED"
@@ -5015,31 +4969,9 @@ fi
CXXFLAGS="$BOOST_CPPFLAGS $CXXFLAGS"
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
# add workaround for problematic boost version
# taken from ax_boost_base.m4
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <boost/version.hpp>
int
main ()
{
(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < (106000))]));
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
else
QBT_ADD_DEFINES="$QBT_ADD_DEFINES BOOST_NO_CXX11_RVALUE_REFERENCES"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
# Check whether --with-boost-system was given.
@@ -5392,7 +5324,7 @@ else
libtorrent_LIBS=$pkg_cv_libtorrent_LIBS
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS"
CPPFLAGS="$libtorrent_CFLAGS $CPPFLAGS"
LIBS="$libtorrent_LIBS $LIBS"
fi
@@ -5485,116 +5417,10 @@ else
zlib_LIBS=$pkg_cv_zlib_LIBS
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
CXXFLAGS="$zlib_CFLAGS $CXXFLAGS"
CPPFLAGS="$zlib_CFLAGS $CPPFLAGS"
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.
@@ -5681,15 +5507,15 @@ extract() {
for i in $string; do
case "$(echo "$i" | cut -c1)" in
'') ;;
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";;
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";;
esac
done
IFS=$SAVEIFS
}
extract "$CFLAGS $CXXFLAGS"
extract "$CFLAGS $CPPFLAGS $CXXFLAGS"
QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
# Substitute the values of these vars in conf.pri.in
@@ -5700,8 +5526,6 @@ QBT_ADD_DEFINES="$QBT_ADD_DEFINES $QBT_CONF_DEFINES"
ac_config_files="$ac_config_files conf.pri"
cat >confcache <<\_ACEOF
@@ -6274,7 +6098,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v4.1.6, which was
This file was extended by qbittorrent $as_me v4.0.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6332,7 +6156,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.6
qbittorrent config.status v4.0.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -6440,7 +6264,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
#
# INIT-COMMANDS
#
AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
_ACEOF
@@ -6885,35 +6709,29 @@ $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.
# 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
case $CONFIG_FILES in
*\'*) eval set x "$CONFIG_FILES" ;;
*) set x $CONFIG_FILES ;;
esac
shift
# Used to flag and report bootstrapping failures.
am_rc=0
for am_mf
for mf
do
# Strip MF so we end up with the name of the file.
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
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
# limit of 2048, but all sed's we know have understand at least 4000.
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" |
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 '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -6931,48 +6749,53 @@ $as_echo X"$am_mf" |
q
}
s/.*/./; q'`
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 '/^.*\/\([^/][^/]*\)\/*$/{
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\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\/\(\/\/\)$/{
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\/\(\/\).*/{
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
{ 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=$?
as_dir=$dirpart/$fdir; as_fn_mkdir_p
# echo "creating $dirpart/$file"
echo '# dummy' > "$dirpart/$file"
done
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
}
;;
@@ -7590,7 +7413,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v4.1.6, which was
This file was extended by qbittorrent $as_me v4.0.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7648,7 +7471,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.6
qbittorrent config.status v4.0.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -7756,7 +7579,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
#
# INIT-COMMANDS
#
AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
_ACEOF
@@ -8202,35 +8025,29 @@ $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.
# 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
case $CONFIG_FILES in
*\'*) eval set x "$CONFIG_FILES" ;;
*) set x $CONFIG_FILES ;;
esac
shift
# Used to flag and report bootstrapping failures.
am_rc=0
for am_mf
for mf
do
# Strip MF so we end up with the name of the file.
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
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
# limit of 2048, but all sed's we know have understand at least 4000.
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" |
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 '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -8248,48 +8065,53 @@ $as_echo X"$am_mf" |
q
}
s/.*/./; q'`
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 '/^.*\/\([^/][^/]*\)\/*$/{
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\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\/\(\/\/\)$/{
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\/\(\/\).*/{
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
{ 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=$?
as_dir=$dirpart/$fdir; as_fn_mkdir_p
# echo "creating $dirpart/$file"
echo '# dummy' > "$dirpart/$file"
done
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.6], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_INIT([qbittorrent], [v4.0.4], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
@@ -8,9 +8,7 @@ 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
@@ -26,12 +24,6 @@ AC_ARG_ENABLE(debug,
[],
[enable_debug=no])
AC_ARG_ENABLE(stacktrace,
[AS_HELP_STRING([--enable-stacktrace],
[Enable stacktrace feature (default=auto)])],
[],
[enable_stacktrace=auto])
AC_ARG_ENABLE(gui,
[AS_HELP_STRING([--disable-gui],
[Disable the GUI for headless running. Disables QtDBus and the GeoIP Database.])],
@@ -88,23 +80,6 @@ AS_CASE(["x$enable_debug"],
[AC_MSG_RESULT([$enable_debug])
AC_MSG_ERROR([Unknown option "$enable_debug". Use either "yes" or "no".])])
AC_MSG_CHECKING([whether to enable the stacktrace feature])
AS_CASE(["x$enable_stacktrace"],
["xno"],
[AC_MSG_RESULT([no])
QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG stacktrace"],
["xyes"],
[AC_MSG_RESULT([yes])
QBT_ADD_CONFIG="$QBT_ADD_CONFIG stacktrace"],
["xauto"],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <execinfo.h>]])],
[AC_MSG_RESULT([yes])
QBT_ADD_CONFIG="$QBT_ADD_CONFIG stacktrace"],
[AC_MSG_RESULT([no])
QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG stacktrace"])],
[AC_MSG_RESULT([$enable_stacktrace])
AC_MSG_ERROR([Unknown option "$enable_stacktrace". Use either "yes" or "no".])])
AC_MSG_CHECKING([whether to enable the GUI])
AS_CASE(["x$enable_gui"],
["xyes"],
@@ -164,21 +139,12 @@ AS_CASE(["x$enable_qt_dbus"],
AX_BOOST_BASE([1.35],
[AC_MSG_NOTICE([Boost CXXFLAGS: "$BOOST_CPPFLAGS"])
AC_MSG_NOTICE([Boost LDFLAGS: "$BOOST_LDFLAGS"])],
[AC_MSG_NOTICE([Boost CPPFLAGS: "$BOOST_CPPFLAGS"
Boost LDFLAGS: "$BOOST_LDFLAGS"])],
[AC_MSG_ERROR([Could not find Boost])])
CXXFLAGS="$BOOST_CPPFLAGS $CXXFLAGS"
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
# add workaround for problematic boost version
# taken from ax_boost_base.m4
m4_define([DETECT_BOOST_VERSION_PROGRAM],
[AC_LANG_PROGRAM([[#include <boost/version.hpp>]],
[[(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < ($1))]));]])])
AC_COMPILE_IFELSE([DETECT_BOOST_VERSION_PROGRAM(106000)], [],
[QBT_ADD_DEFINES="$QBT_ADD_DEFINES BOOST_NO_CXX11_RVALUE_REFERENCES"])
AX_BOOST_SYSTEM()
AC_MSG_NOTICE([Boost.System LIB: "$BOOST_SYSTEM_LIB"])
LIBS="$BOOST_SYSTEM_LIB $LIBS"
@@ -196,46 +162,14 @@ AS_CASE(["x$with_qtsingleapplication"],
PKG_CHECK_MODULES(libtorrent,
[libtorrent-rasterbar >= 1.0.6],
[CXXFLAGS="$libtorrent_CFLAGS $CXXFLAGS"
[CPPFLAGS="$libtorrent_CFLAGS $CPPFLAGS"
LIBS="$libtorrent_LIBS $LIBS"])
PKG_CHECK_MODULES(zlib,
[zlib >= 1.2.5.2],
[CXXFLAGS="$zlib_CFLAGS $CXXFLAGS"
[CPPFLAGS="$zlib_CFLAGS $CPPFLAGS"
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])
@@ -262,20 +196,18 @@ extract() {
for i in $string; do
case "$(echo "$i" | cut -c1)" in
'') ;;
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";;
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";;
esac
done
IFS=$SAVEIFS
}
extract "$CFLAGS $CXXFLAGS"
extract "$CFLAGS $CPPFLAGS $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)

2
dist/CMakeLists.txt vendored
View File

@@ -1,5 +1,3 @@
find_package(Qt5Widgets ${requiredQtVersion}) # to conditionally install desktop-related files
if (APPLE)
add_subdirectory(mac)
else (APPLE)

22
dist/mac/Info.plist vendored
View File

@@ -2,10 +2,6 @@
<!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>
@@ -25,10 +21,6 @@
<array>
<string>org.bittorrent.torrent</string>
</array>
<key>NSExportableTypes</key>
<array>
<string>org.bittorrent.torrent</string>
</array>
<key>LSIsAppleDefaultForType</key>
<true/>
</dict>
@@ -36,8 +28,6 @@
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLSchemes</key>
<array>
<string>magnet</string>
@@ -55,19 +45,21 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.1.6</string>
<string>4.0.4</string>
<key>CFBundleSignature</key>
<string>qBit</string>
<key>CFBundleExecutable</key>
<string>@EXECUTABLE@</string>
<key>CFBundleIdentifier</key>
<string>org.qbittorrent.qBittorrent</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}.0</string>
<string>org.qbittorrent</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-2019 The qBittorrent project</string>
<string>Copyright © 2006-2018 The qBittorrent project</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>

Binary file not shown.

View File

@@ -1,42 +1,36 @@
if (NOT Qt5Widgets_FOUND)
feature_option(SYSTEMD "Install systemd service file (headless only)" OFF)
if (SYSTEMD)
if (NOT Systemd_SERVICES_INSTALL_DIR)
find_package(Systemd)
if (NOT Systemd_FOUND)
message(FATAL_ERROR "Could not locate systemd services install dir."
" Either pass -DSystemd_SERVICES_INSTALL_DIR=/path/to/systemd/services option or install systemd pkg-config")
endif(NOT Systemd_FOUND)
endif(NOT Systemd_SERVICES_INSTALL_DIR)
if (SYSTEMD)
find_package(Systemd)
if (SYSTEMD_FOUND)
set(EXPAND_BINDIR ${CMAKE_INSTALL_FULL_BINDIR})
configure_file(systemd/qbittorrent-nox@.service.in ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service
DESTINATION ${Systemd_SERVICES_INSTALL_DIR}
DESTINATION ${SYSTEMD_SERVICES_INSTALL_DIR}
COMPONENT data)
endif(SYSTEMD)
endif()
endif(SYSTEMD_FOUND)
endif(SYSTEMD)
if (Qt5Widgets_FOUND)
if (GUI)
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent.1)
else (Qt5Widgets_FOUND)
else (GUI)
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent-nox.1)
endif (Qt5Widgets_FOUND)
endif (GUI)
install(FILES ${MAN_FILES}
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
COMPONENT doc)
if (Qt5Widgets_FOUND)
if (GUI)
install(DIRECTORY menuicons/
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
FILES_MATCHING PATTERN "*.png")
install(FILES org.qbittorrent.qBittorrent.desktop
install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qbittorrent.desktop
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
COMPONENT data)
install(FILES org.qbittorrent.qBittorrent.appdata.xml
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo/
install(FILES qbittorrent.appdata.xml
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/appdata/
COMPONENT data)
install(FILES

View File

@@ -1,75 +0,0 @@
<?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>

66
dist/unix/qbittorrent.appdata.xml vendored Normal file
View File

@@ -0,0 +1,66 @@
<?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.6"
!define PROG_VERSION "4.0.4"
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
@@ -50,7 +50,7 @@ XPStyle on
;Installer Version Information
VIAddVersionKey "ProductName" "qBittorrent"
VIAddVersionKey "CompanyName" "The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2019 The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2018 The qBittorrent project"
VIAddVersionKey "FileDescription" "qBittorrent - A Bittorrent Client"
VIAddVersionKey "FileVersion" "${PROG_VERSION}"

View File

@@ -36,16 +36,3 @@ 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

@@ -3,6 +3,7 @@ TEMPLATE = subdirs
SUBDIRS += src
include(version.pri)
include(qm_gen.pri)
# Make target to create release tarball. Use 'make tarball'
tarball.commands += rm -fR ../$${PROJECT_NAME}-$${PROJECT_VERSION}/ &&
@@ -16,11 +17,3 @@ tarball.commands += xz -f $${PROJECT_NAME}-$${PROJECT_VERSION}.tar &&
tarball.commands += rm -fR $${PROJECT_NAME}-$${PROJECT_VERSION}
QMAKE_EXTRA_TARGETS += tarball
# Translations included here (at top level) is to avoid regenerating the .qm files
# every time when src.pro is processed
include(src/lang/lang.pri)
# For Qt Creator beautifier
DISTFILES += \
uncrustify.cfg

21
qm_gen.pri Normal file
View File

@@ -0,0 +1,21 @@
TS_IN = $$fromfile(src/src.pro,TRANSLATIONS)
TS_IN_NOEXT = $$replace(TS_IN,".ts","")
isEmpty(QMAKE_LRELEASE) {
win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\\lrelease.exe
else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
unix {
equals(QT_MAJOR_VERSION, 5) {
!exists($$QMAKE_LRELEASE) { QMAKE_LRELEASE = lrelease-qt5 }
}
} else {
!exists($$QMAKE_LRELEASE) { QMAKE_LRELEASE = lrelease }
}
}
message("Building translations")
for(L,TS_IN_NOEXT) {
message("Processing $${L}")
system("$$QMAKE_LRELEASE -silent src/$${L}.ts -qm src/$${L}.qm")
!exists("src/$${L}.qm"):error("Building translations failed, cannot continue")
}

View File

@@ -1,53 +1,40 @@
set(CMAKE_CXX_STANDARD_REQUIRED True)
# If C++14 is available, use it as libtorent ABI depends on 11/14 version
if (cxx_std_14 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
message(STATUS "Building in C++14 mode")
set(CMAKE_CXX_STANDARD "14")
else()
message(STATUS "Building in C++11 mode")
set(CMAKE_CXX_STANDARD "11")
endif()
set(CMAKE_CXX_STANDARD "11")
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
include(MacroQbtCompilerSettings)
qbt_set_compiler_options()
include(MacroLinkQtComponents)
include(QbtTargetSources)
find_package(Boost ${requiredBoostVersion} REQUIRED)
find_package(LibtorrentRasterbar REQUIRED)
if (Boost_VERSION VERSION_LESS 106000)
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
endif()
# Qt
list(APPEND QBT_QT_COMPONENTS Core Network Xml)
if (GUI)
list (APPEND QBT_QT_COMPONENTS Gui Svg Widgets)
if (WIN32)
list (APPEND QBT_QT_COMPONENTS WinExtras)
endif(WIN32)
if (APPLE)
list (APPEND QBT_GUI_OPTIONAL_LINK_LIBRARIES objc)
list (APPEND QBT_QT_COMPONENTS MacExtras)
endif (APPLE)
endif (GUI)
if (DBUS)
list (APPEND QBT_QT_COMPONENTS DBus)
endif (DBUS)
find_package(Qt5 5.5.1 COMPONENTS ${QBT_QT_COMPONENTS} REQUIRED)
find_package(Qt5 ${requiredQtVersion} REQUIRED COMPONENTS Core Network Xml LinguistTools)
find_package(Qt5Widgets ${requiredQtVersion})
if (Qt5Widgets_FOUND)
find_package(Qt5DBus ${requiredQtVersion})
else()
add_definitions(-DDISABLE_GUI)
endif()
set_package_properties(Qt5Widgets PROPERTIES
DESCRIPTION "Set of components for creating classic desktop-style UIs for the Qt5 framework"
PURPOSE "Enables qBittorrent GUI. Unneeded for headless configuration."
TYPE OPTIONAL
)
set_package_properties(Qt5DBus PROPERTIES
DESCRIPTION "Qt5 module for inter-process communication over the D-Bus protocol"
PURPOSE "Enables communication with other system components (e.g. notification service) via D-Bus. "
TYPE RECOMMENDED
)
if (GUI AND APPLE)
# Fix MOC inability to detect macOS. This seems to only affect cmake.
# Relevant issue: https://bugreports.qt.io/browse/QTBUG-58325
set(CMAKE_AUTOMOC_MOC_OPTIONS ${CMAKE_AUTOMOC_MOC_OPTIONS} -DQ_OS_MAC)
endif ()
set(CMAKE_AUTOMOC True)
list(APPEND CMAKE_AUTORCC_OPTIONS -compress 9 -threshold 5)
if (APPLE)
# Workaround CMake bug (autogen does not pass required parameters to moc)
# Relevant issue: https://gitlab.kitware.com/cmake/cmake/issues/18041
list(APPEND CMAKE_AUTOMOC_MOC_OPTIONS -DQ_OS_MAC -DQ_OS_DARWIN)
endif ()
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
@@ -56,34 +43,56 @@ add_definitions(-DQT_NO_CAST_TO_ASCII)
# Efficient construction for QString & QByteArray (Qt >= 4.8)
add_definitions(-DQT_USE_QSTRINGBUILDER)
if (CMAKE_BUILD_TYPE MATCHES "Debug")
if (NOT GUI)
add_definitions(-DDISABLE_GUI -DDISABLE_COUNTRIES_RESOLUTION)
endif (NOT GUI)
if (NOT WEBUI)
add_definitions(-DDISABLE_WEBUI)
endif (NOT WEBUI)
if (STACKTRACE_WIN)
add_definitions(-DSTACKTRACE_WIN)
endif(STACKTRACE_WIN)
# nogui {
# TARGET = qbittorrent-nox
# } else {
# CONFIG(static) {
# DEFINES += QBT_STATIC_QT
# QTPLUGIN += qico
# }
# TARGET = qbittorrent
# }
if (UNIX AND NOT APPLE)
add_compile_options(-Wformat -Wformat-security)
endif ()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Project is built in DEBUG mode.")
else()
else (CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Project is built in RELEASE mode.")
message(STATUS "Disabling debug output.")
add_definitions(-DQT_NO_DEBUG_OUTPUT)
endif()
endif (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(QBT_USE_GUI ${GUI})
set(QBT_USE_WEBUI ${WEBUI})
configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
find_package(QtSingleApplication)
set_package_properties(QtSingleApplication PROPERTIES
URL "https://code.qt.io/cgit/qt-solutions/qt-solutions.git/"
DESCRIPTION "Qt library to start applications only once per user"
TYPE RECOMMENDED
PURPOSE "Use the system qtsingleapplication library or shipped one otherwise"
)
if (NOT QtSingleApplication_FOUND)
if (SYSTEM_QTSINGLEAPPLICATION)
find_package(QtSingleApplication REQUIRED)
else (SYSTEM_QTSINGLEAPPLICATION)
add_subdirectory(app/qtsingleapplication)
endif ()
endif (SYSTEM_QTSINGLEAPPLICATION)
add_subdirectory(app)
add_subdirectory(base)
if (Qt5Widgets_FOUND)
if (GUI)
add_subdirectory(gui)
endif ()
endif (GUI)
if (WEBUI)
add_subdirectory(webui)

View File

@@ -1,41 +1,43 @@
add_executable(qBittorrent
project(qbt_executable)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(QBT_APP_HEADERS
application.h
cmdoptions.h
filelogger.h
upgrade.h
)
set(QBT_APP_SOURCES
application.cpp
cmdoptions.cpp
filelogger.cpp
main.cpp
)
target_include_directories(qBittorrent PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(qBittorrent
PRIVATE
qbt_base
)
set_target_properties(qBittorrent
PROPERTIES
AUTOUIC True
AUTORCC True
MACOSX_BUNDLE True
)
# translations
include(QbtTranslations)
file(GLOB QBT_TS_FILES ../lang/*.ts)
qbt_add_translations(qBittorrent QRC_FILE "../lang/lang.qrc" TS_FILES ${QBT_TS_FILES})
get_filename_component(QBT_QM_FILES_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../lang" ABSOLUTE)
set_source_files_properties(${QBT_TS_FILES} PROPERTIES OUTPUT_LOCATION "${QBT_QM_FILES_BINARY_DIR}")
if (WEBUI)
file(GLOB QBT_WEBUI_TS_FILES ../webui/www/translations/*.ts)
qbt_add_translations(qBittorrent QRC_FILE "../webui/www/translations/webui_translations.qrc" TS_FILES ${QBT_WEBUI_TS_FILES})
endif()
find_package(Qt5 COMPONENTS LinguistTools REQUIRED)
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
get_filename_component(_lang_qrc_src "${CMAKE_CURRENT_SOURCE_DIR}/../lang.qrc" ABSOLUTE)
get_filename_component(_lang_qrc_dst "${CMAKE_CURRENT_BINARY_DIR}/../lang.qrc" ABSOLUTE)
get_filename_component(_lang_qrc_dst_dir "${CMAKE_CURRENT_BINARY_DIR}/../" ABSOLUTE)
message(STATUS "copying ${_lang_qrc_src} -> ${_lang_qrc_dst}")
file(COPY ${_lang_qrc_src} DESTINATION ${_lang_qrc_dst_dir})
set_source_files_properties("${_lang_qrc_dst}" PROPERTIES GENERATED True)
foreach(qm_file ${QBT_QM_FILES})
set_source_files_properties("${_lang_qrc_dst}" PROPERTIES OBJECT_DEPENDS ${qm_file})
endforeach()
set(QBT_APP_RESOURCES
../icons/icons.qrc
../searchengine/searchengine.qrc
../icons.qrc
../searchengine.qrc
"${_lang_qrc_dst}"
)
# With AUTORCC rcc is ran by cmake before language files are generated,
@@ -44,41 +46,51 @@ qt5_add_resources(QBT_APP_RESOURCE_SOURCE ${QBT_APP_RESOURCES})
if (WIN32)
if (MINGW)
target_sources(qBittorrent PRIVATE ../qbittorrent_mingw.rc)
list (APPEND QBT_APP_SOURCES ../qbittorrent_mingw.rc)
else (MINGW)
target_sources(qBittorrent PRIVATE ../qbittorrent.rc)
list (APPEND QBT_APP_SOURCES ../qbittorrent.rc)
endif (MINGW)
target_sources(qBittorrent PRIVATE ../qbittorrent.exe.manifest)
list(APPEND QBT_APP_SOURCES ../qbittorrent.exe.manifest)
endif (WIN32)
if (STACKTRACE)
if (UNIX)
target_sources(qBittorrent PRIVATE stacktrace.h)
else (UNIX)
target_sources(qBittorrent PRIVATE stacktrace_win.h)
if (Qt5Widgets_FOUND)
target_sources(qBittorrent PRIVATE stacktracedialog.h)
endif (Qt5Widgets_FOUND)
endif (UNIX)
endif (STACKTRACE)
if (UNIX)
list(APPEND QBT_APP_HEADERS stacktrace.h)
endif (UNIX)
if (STACKTRACE_WIN)
list(APPEND QBT_APP_HEADERS stacktrace_win.h)
if (GUI)
list(APPEND QBT_APP_HEADERS stacktrace_win_dlg.h)
endif (GUI)
endif (STACKTRACE_WIN)
if (Qt5Widgets_FOUND)
target_link_libraries(qBittorrent PRIVATE qbt_searchengine qbt_gui)
set_target_properties(qBittorrent
PROPERTIES
OUTPUT_NAME qbittorrent
WIN32_EXECUTABLE True
# usesystemqtsingleapplication {
# nogui {
# CONFIG += qtsinglecoreapplication
# } else {
# CONFIG += qtsingleapplication
# }
# } else {
# nogui {
# include(qtsingleapplication/qtsinglecoreapplication.pri)
# } else {
# include(qtsingleapplication/qtsingleapplication.pri)
# }
# }
# upgrade code
list(APPEND QBT_APP_HEADERS upgrade.h)
list(APPEND QBT_TARGET_LIBRARIES qbt_base)
if (GUI)
list(APPEND QBT_TARGET_LIBRARIES qbt_searchengine qbt_gui)
include_directories(../gui
${CMAKE_CURRENT_BINARY_DIR}/../gui
)
else(Qt5Widgets_FOUND)
set_target_properties(qBittorrent
PROPERTIES
OUTPUT_NAME qbittorrent-nox
)
endif (Qt5Widgets_FOUND)
endif (GUI)
if (WEBUI)
target_link_libraries(qBittorrent PRIVATE qbt_webui)
list(APPEND QBT_TARGET_LIBRARIES qbt_webui)
endif (WEBUI)
# we have to include resources into the bundle
@@ -130,11 +142,30 @@ if (APPLE)
PROPERTIES MACOSX_PACKAGE_LOCATION translations)
endif (APPLE)
target_sources(qBittorrent PRIVATE ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE})
add_executable(qBittorrent ${QBT_APP_HEADERS} ${QBT_APP_SOURCES} ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE})
if (GUI)
set_target_properties(qBittorrent
PROPERTIES
OUTPUT_NAME qbittorrent
WIN32_EXECUTABLE True
)
else (GUI)
set_target_properties(qBittorrent
PROPERTIES
OUTPUT_NAME qbittorrent-nox
)
endif (GUI)
set_target_properties(qBittorrent
PROPERTIES
AUTOUIC True
AUTORCC True
MACOSX_BUNDLE True
)
get_target_property(QBT_EXECUTABLE_NAME qBittorrent OUTPUT_NAME)
target_link_libraries(qBittorrent PRIVATE ${QBT_TARGET_LIBRARIES} QtSingleApplication::QtSingleApplication)
target_link_libraries(qBittorrent ${QBT_TARGET_LIBRARIES} QtSingleApplication::QtSingleApplication)
if (APPLE)
set(qbt_BUNDLE_NAME ${QBT_EXECUTABLE_NAME})
@@ -155,7 +186,6 @@ install(TARGETS qBittorrent
BUNDLE DESTINATION .
COMPONENT runtime)
if (Qt5Widgets_FOUND AND APPLE)
find_package(Qt5Svg REQUIRED)
if (GUI AND APPLE)
include(bundle)
endif (Qt5Widgets_FOUND AND APPLE)
endif (GUI AND APPLE)

View File

@@ -25,16 +25,12 @@ SOURCES += \
$$PWD/filelogger.cpp \
$$PWD/main.cpp
stacktrace {
unix {
HEADERS += $$PWD/stacktrace.h
}
else {
HEADERS += $$PWD/stacktrace_win.h
!nogui {
HEADERS += $$PWD/stacktracedialog.h
FORMS += $$PWD/stacktracedialog.ui
}
unix: HEADERS += $$PWD/stacktrace.h
strace_win {
HEADERS += $$PWD/stacktrace_win.h
!nogui {
HEADERS += $$PWD/stacktrace_win_dlg.h
FORMS += $$PWD/stacktrace_win_dlg.ui
}
}

View File

@@ -27,70 +27,62 @@
* exception statement from your version.
*/
#include "application.h"
#include <algorithm>
#include <QAtomicInt>
#include <QDebug>
#include <QLibraryInfo>
#include <QFileInfo>
#include <QLocale>
#include <QProcess>
#include <QLibraryInfo>
#include <QSysInfo>
#ifdef Q_OS_WIN
#include <memory>
#include <Shellapi.h>
#endif
#include <QProcess>
#include <QAtomicInt>
#ifndef DISABLE_GUI
#include <QMessageBox>
#include "gui/guiiconprovider.h"
#ifdef Q_OS_WIN
#include <QSessionManager>
#include <windows.h>
#include <QSharedMemory>
#include <QSessionManager>
#endif // Q_OS_WIN
#ifdef Q_OS_MAC
#include <QFileOpenEvent>
#include <QFont>
#include <QUrl>
#endif // Q_OS_MAC
#include "addnewtorrentdialog.h"
#include "gui/guiiconprovider.h"
#include "mainwindow.h"
#include "shutdownconfirmdialog.h"
#include "addnewtorrentdialog.h"
#include "shutdownconfirmdlg.h"
#else // DISABLE_GUI
#include <cstdio>
#include <iostream>
#endif // DISABLE_GUI
#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"
#include "base/net/geoipmanager.h"
#include "base/net/proxyconfigurationmanager.h"
#include "base/net/smtp.h"
#include "base/preferences.h"
#include "base/profile.h"
#include "base/rss/rss_autodownloader.h"
#include "base/rss/rss_session.h"
#include "base/scanfoldersmodel.h"
#include "base/search/searchpluginmanager.h"
#include "base/settingsstorage.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "filelogger.h"
#ifndef DISABLE_WEBUI
#include "webui/webui.h"
#endif
#include "application.h"
#include "filelogger.h"
#include "base/logger.h"
#include "base/preferences.h"
#include "base/settingsstorage.h"
#include "base/profile.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/iconprovider.h"
#include "base/scanfoldersmodel.h"
#include "base/net/smtp.h"
#include "base/net/downloadmanager.h"
#include "base/net/geoipmanager.h"
#include "base/net/proxyconfigurationmanager.h"
#include "base/bittorrent/session.h"
#include "base/bittorrent/torrenthandle.h"
#include "base/rss/rss_autodownloader.h"
#include "base/rss/rss_session.h"
namespace
{
#define SETTINGS_KEY(name) "Application/" name
// FileLogger properties keys
#define FILELOGGER_SETTINGS_KEY(name) QStringLiteral(SETTINGS_KEY("FileLogger/") name)
#define FILELOGGER_SETTINGS_KEY(name) SETTINGS_KEY("FileLogger/") name
const QString KEY_FILELOGGER_ENABLED = FILELOGGER_SETTINGS_KEY("Enabled");
const QString KEY_FILELOGGER_PATH = FILELOGGER_SETTINGS_KEY("Path");
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
@@ -99,13 +91,13 @@ namespace
const QString KEY_FILELOGGER_AGE = FILELOGGER_SETTINGS_KEY("Age");
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
// just a shortcut
//just a shortcut
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
const QString LOG_FOLDER = QStringLiteral("logs");
const QChar PARAMS_SEPARATOR = '|';
const QString LOG_FOLDER("logs");
const char PARAMS_SEPARATOR[] = "|";
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QStringLiteral("profile");
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QLatin1String("profile");
const int MIN_FILELOG_SIZE = 1024; // 1KiB
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
@@ -124,7 +116,6 @@ 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
@@ -146,17 +137,14 @@ 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)
connect(this, &QGuiApplication::commitDataRequest, this, &Application::shutdownCleanup, Qt::DirectConnection);
connect(this, SIGNAL(commitDataRequest(QSessionManager &)), this, SLOT(shutdownCleanup(QSessionManager &)), Qt::DirectConnection);
#endif
connect(this, &Application::messageReceived, this, &Application::processMessage);
connect(this, &QCoreApplication::aboutToQuit, this, &Application::cleanup);
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &)));
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
if (isFileLoggerEnabled())
m_fileLogger = new FileLogger(fileLoggerPath(), isFileLoggerBackup(), fileLoggerMaxSize(), isFileLoggerDeleteOld(), fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
@@ -262,17 +250,17 @@ void Application::setFileLoggerAge(const int value)
int Application::fileLoggerAgeType() const
{
int val = settings()->loadValue(KEY_FILELOGGER_AGETYPE, 1).toInt();
return ((val < 0) || (val > 2)) ? 1 : val;
return (val < 0 || val > 2) ? 1 : val;
}
void Application::setFileLoggerAgeType(const int value)
{
settings()->storeValue(KEY_FILELOGGER_AGETYPE, ((value < 0) || (value > 2)) ? 1 : value);
settings()->storeValue(KEY_FILELOGGER_AGETYPE, (value < 0 || value > 2) ? 1 : value);
}
void Application::processMessage(const QString &message)
{
QStringList params = message.split(PARAMS_SEPARATOR, QString::SkipEmptyParts);
QStringList params = message.split(QLatin1String(PARAMS_SEPARATOR), QString::SkipEmptyParts);
// If Application is not running (i.e., other
// components are not ready) store params
if (m_running)
@@ -281,70 +269,38 @@ void Application::processMessage(const QString &message)
m_paramsQueue.append(params);
}
void Application::runExternalProgram(const BitTorrent::TorrentHandle *torrent) const
void Application::runExternalProgram(BitTorrent::TorrentHandle *const torrent) const
{
QString program = Preferences::instance()->getAutoRunProgram().trimmed();
QString program = Preferences::instance()->getAutoRunProgram();
program.replace("%N", torrent->name());
program.replace("%L", torrent->category());
QStringList tags = torrent->tags().toList();
std::sort(tags.begin(), tags.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
program.replace("%G", tags.join(','));
#if defined(Q_OS_WIN)
const auto chopPathSep = [](const QString &str) -> QString
{
if (str.endsWith('\\'))
return str.mid(0, (str.length() -1));
return str;
};
program.replace("%F", chopPathSep(Utils::Fs::toNativePath(torrent->contentPath())));
program.replace("%R", chopPathSep(Utils::Fs::toNativePath(torrent->rootPath())));
program.replace("%D", chopPathSep(Utils::Fs::toNativePath(torrent->savePath())));
#else
program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
#endif
program.replace("%C", QString::number(torrent->filesCount()));
program.replace("%Z", QString::number(torrent->totalSize()));
program.replace("%T", torrent->currentTracker());
program.replace("%I", torrent->hash());
Logger *logger = Logger::instance();
logger->addMessage(tr("Torrent: %1, running external program, command: %2").arg(torrent->name(), program));
logger->addMessage(tr("Torrent: %1, running external program, command: %2").arg(torrent->name()).arg(program));
#if defined(Q_OS_WIN)
std::unique_ptr<wchar_t[]> programWchar(new wchar_t[program.length() + 1] {});
program.toWCharArray(programWchar.get());
// Need to split arguments manually because QProcess::startDetached(QString)
// will strip off empty parameters.
// E.g. `python.exe "1" "" "3"` will become `python.exe "1" "3"`
int argCount = 0;
LPWSTR *args = ::CommandLineToArgvW(programWchar.get(), &argCount);
QStringList argList;
for (int i = 1; i < argCount; ++i)
argList += QString::fromWCharArray(args[i]);
QProcess::startDetached(QString::fromWCharArray(args[0]), argList);
::LocalFree(args);
#else
#if defined(Q_OS_UNIX)
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
#else
QProcess::startDetached(program);
#endif
}
void Application::sendNotificationEmail(const BitTorrent::TorrentHandle *torrent)
{
// Prepare mail content
const QString content = tr("Torrent name: %1").arg(torrent->name()) + '\n'
+ tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + '\n'
const QString content = tr("Torrent name: %1").arg(torrent->name()) + "\n"
+ tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + "\n"
+ tr("Save path: %1").arg(torrent->savePath()) + "\n\n"
+ tr("The torrent was downloaded in %1.", "The torrent was downloaded in 1 hour and 20 seconds")
.arg(Utils::Misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n"
+ tr("Thank you for using qBittorrent.") + '\n';
+ tr("Thank you for using qBittorrent.") + "\n";
// Send the notification email
const Preferences *pref = Preferences::instance();
@@ -395,7 +351,7 @@ void Application::allTorrentsFinished()
// do nothing & skip confirm
}
else {
if (!ShutdownConfirmDialog::askForConfirmation(m_window, action)) return;
if (!ShutdownConfirmDlg::askForConfirmation(m_window, action)) return;
}
#endif // DISABLE_GUI
@@ -416,7 +372,7 @@ void Application::allTorrentsFinished()
bool Application::sendParams(const QStringList &params)
{
return sendMessage(params.join(PARAMS_SEPARATOR));
return sendMessage(params.join(QLatin1String(PARAMS_SEPARATOR)));
}
// As program parameters, we can get paths or urls.
@@ -434,7 +390,7 @@ void Application::processParams(const QStringList &params)
BitTorrent::AddTorrentParams torrentParams;
TriStateBool skipTorrentDialog;
for (QString param : params) {
foreach (QString param, params) {
param = param.trimmed();
// Process strings indicating options specified by the user.
@@ -445,7 +401,7 @@ void Application::processParams(const QStringList &params)
}
if (param.startsWith(QLatin1String("@addPaused="))) {
torrentParams.addPaused = param.midRef(11).toInt() ? TriStateBool::True : TriStateBool::False;
torrentParams.addPaused = param.mid(11).toInt() ? TriStateBool::True : TriStateBool::False;
continue;
}
@@ -470,7 +426,7 @@ void Application::processParams(const QStringList &params)
}
if (param.startsWith(QLatin1String("@skipDialog="))) {
skipTorrentDialog = param.midRef(12).toInt() ? TriStateBool::True : TriStateBool::False;
skipTorrentDialog = param.mid(12).toInt() ? TriStateBool::True : TriStateBool::False;
continue;
}
@@ -501,57 +457,38 @@ int Application::exec(const QStringList &params)
GuiIconProvider::initInstance();
#endif
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);
BitTorrent::Session::initInstance();
connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const)));
connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(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
}
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;
}
new RSS::Session; // create RSS::Session singleton
new RSS::AutoDownloader; // create RSS::AutoDownloader singleton
#ifdef DISABLE_GUI
#ifndef DISABLE_WEBUI
Preferences *const pref = Preferences::instance();
Preferences* const pref = Preferences::instance();
// Display some information to the user
const QString mesg = QString("\n******** %1 ********\n").arg(tr("Information"))
+ tr("To control qBittorrent, access the Web UI at %1")
.arg(QString("http://localhost:") + QString::number(pref->getWebUiPort())) + '\n'
+ tr("The Web UI administrator user name is: %1").arg(pref->getWebUiUsername()) + '\n';
printf("%s", qUtf8Printable(mesg));
std::cout << std::endl << "******** " << qPrintable(tr("Information")) << " ********" << std::endl;
std::cout << qPrintable(tr("To control qBittorrent, access the Web UI at http://localhost:%1").arg(QString::number(pref->getWebUiPort()))) << std::endl;
std::cout << qPrintable(tr("The Web UI administrator user name is: %1").arg(pref->getWebUiUsername())) << std::endl;
qDebug() << "Password:" << pref->getWebUiPassword();
if (pref->getWebUiPassword() == "f6fdffe48c908deb0f4c3bd36c032e72") {
const QString warning = tr("The Web UI administrator password is still the default one: %1").arg("adminadmin") + '\n'
+ tr("This is a security risk, please consider changing your password from program preferences.") + '\n';
printf("%s", qUtf8Printable(warning));
std::cout << qPrintable(tr("The Web UI administrator password is still the default one: %1").arg("adminadmin")) << std::endl;
std::cout << qPrintable(tr("This is a security risk, please consider changing your password from program preferences.")) << std::endl;
}
#endif // DISABLE_WEBUI
#else
@@ -636,7 +573,7 @@ bool Application::notify(QObject *receiver, QEvent *event)
void Application::initializeTranslation()
{
Preferences *const pref = Preferences::instance();
Preferences* const pref = Preferences::instance();
// Load translation
QString localeStr = pref->getLocale();
@@ -689,7 +626,7 @@ void Application::shutdownCleanup(QSessionManager &manager)
// According to the qt docs we shouldn't call quit() inside a slot.
// aboutToQuit() is never emitted if the user hits "Cancel" in
// the above dialog.
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
QTimer::singleShot(0, qApp, SLOT(quit()));
}
#endif
@@ -702,7 +639,7 @@ void Application::cleanup()
#ifndef DISABLE_GUI
if (m_window) {
// Hide the window and don't leave it on screen as
// Hide the window and not leave it on screen as
// unresponsive. Also for Windows take the WinId
// after it's hidden, because hide() may cause a
// WinId change.
@@ -710,7 +647,7 @@ void Application::cleanup()
#ifdef Q_OS_WIN
typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR);
const auto shutdownBRCreate = Utils::Misc::loadWinAPI<PSHUTDOWNBRCREATE>("User32.dll", "ShutdownBlockReasonCreate");
PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)::GetProcAddress(::GetModuleHandleW(L"User32.dll"), "ShutdownBlockReasonCreate");
// Only available on Vista+
if (shutdownBRCreate)
shutdownBRCreate((HWND)m_window->effectiveWinId(), tr("Saving torrent progress...").toStdWString().c_str());
@@ -746,14 +683,13 @@ void Application::cleanup()
delete m_fileLogger;
Logger::freeInstance();
IconProvider::freeInstance();
SearchPluginManager::freeInstance();
Utils::Fs::removeDirRecursive(Utils::Fs::tempPath());
#ifndef DISABLE_GUI
if (m_window) {
#ifdef Q_OS_WIN
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
const auto shutdownBRDestroy = Utils::Misc::loadWinAPI<PSHUTDOWNBRDESTROY>("User32.dll", "ShutdownBlockReasonDestroy");
PSHUTDOWNBRDESTROY shutdownBRDestroy = (PSHUTDOWNBRDESTROY)::GetProcAddress(::GetModuleHandleW(L"User32.dll"), "ShutdownBlockReasonDestroy");
// Only available on Vista+
if (shutdownBRDestroy)
shutdownBRDestroy((HWND)m_window->effectiveWinId());

View File

@@ -40,13 +40,15 @@ typedef QtSingleApplication BaseApplication;
class MainWindow;
#ifdef Q_OS_WIN
QT_BEGIN_NAMESPACE
class QSessionManager;
QT_END_NAMESPACE
#endif // Q_OS_WIN
#else
#include "qtsinglecoreapplication.h"
typedef QtSingleCoreApplication BaseApplication;
#endif // DISABLE_GUI
#endif
#include "base/utils/misc.h"
#include "cmdoptions.h"
@@ -108,9 +110,9 @@ public:
protected:
#ifndef DISABLE_GUI
#ifdef Q_OS_MAC
bool event(QEvent *) override;
bool event(QEvent *);
#endif
bool notify(QObject *receiver, QEvent *event) override;
bool notify(QObject* receiver, QEvent* event);
#endif
private slots:
@@ -144,7 +146,7 @@ private:
void initializeTranslation();
void processParams(const QStringList &params);
void runExternalProgram(const BitTorrent::TorrentHandle *torrent) const;
void runExternalProgram(BitTorrent::TorrentHandle *const torrent) const;
void sendNotificationEmail(const BitTorrent::TorrentHandle *torrent);
void validateCommandLineParameters();
};

View File

@@ -2,7 +2,7 @@
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2006 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -26,11 +26,13 @@
* 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.
*
* Contact : chris@qbittorrent.org
*/
#include "cmdoptions.h"
#include <cstdio>
#include <iostream>
#include <QDebug>
#include <QFileInfo>
@@ -41,7 +43,6 @@
#include <QMessageBox>
#endif
#include "base/global.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
@@ -99,7 +100,7 @@ namespace
};
// Boolean option.
class BoolOption : protected Option
class BoolOption: protected Option
{
public:
constexpr BoolOption(const char *name, char shortcut = 0)
@@ -117,7 +118,7 @@ namespace
{
QString val = env.value(envVarName());
// we accept "1" and "true" (upper or lower cased) as boolean 'true' values
return ((val == QLatin1String("1")) || (val.toUpper() == QLatin1String("TRUE")));
return (val == QLatin1String("1") || val.toUpper() == QLatin1String("TRUE"));
}
QString usage() const
@@ -136,7 +137,7 @@ namespace
}
// Option with string value. May not have a shortcut
struct StringOption : protected Option
struct StringOption: protected Option
{
public:
constexpr StringOption(const char *name)
@@ -183,7 +184,7 @@ namespace
}
// Option with integer value. May not have a shortcut
class IntOption : protected StringOption
class IntOption: protected StringOption
{
public:
constexpr IntOption(const char *name)
@@ -215,7 +216,7 @@ namespace
int res = val.toInt(&ok);
if (!ok) {
qDebug() << QObject::tr("Expected integer number in environment variable '%1', but got '%2'")
.arg(envVarName(), val);
.arg(envVarName()).arg(val);
return defaultValue;
}
return res;
@@ -229,7 +230,7 @@ namespace
// Option that is explicitly set to true or false, and whose value is undefined when unspecified.
// May not have a shortcut.
class TriStateBoolOption : protected Option
class TriStateBoolOption: protected Option
{
public:
constexpr TriStateBoolOption(const char *name, bool defaultValue)
@@ -259,10 +260,10 @@ namespace
else if (parts.size() == 2) {
QString val = parts[1];
if ((val.toUpper() == QLatin1String("TRUE")) || (val == QLatin1String("1"))) {
if (val.toUpper() == QLatin1String("TRUE") || val == QLatin1String("1")) {
return TriStateBool::True;
}
else if ((val.toUpper() == QLatin1String("FALSE")) || (val == QLatin1String("0"))) {
else if (val.toUpper() == QLatin1String("FALSE") || val == QLatin1String("0")) {
return TriStateBool::False;
}
}
@@ -284,15 +285,15 @@ namespace
else if (val == QLatin1String("-1")) {
return TriStateBool::Undefined;
}
else if ((val.toUpper() == QLatin1String("TRUE")) || (val == QLatin1String("1"))) {
else if (val.toUpper() == QLatin1String("TRUE") || val == QLatin1String("1")) {
return TriStateBool::True;
}
else if ((val.toUpper() == QLatin1String("FALSE")) || (val == QLatin1String("0"))) {
else if (val.toUpper() == QLatin1String("FALSE") || val == QLatin1String("0")) {
return TriStateBool::False;
}
else {
qDebug() << QObject::tr("Expected %1 in environment variable '%2', but got '%3'")
.arg(QLatin1String("true|false"), envVarName(), val);
.arg(QLatin1String("true|false")).arg(envVarName()).arg(val);
return TriStateBool::Undefined;
}
}
@@ -359,7 +360,7 @@ QStringList QBtCommandLineParameters::paramList() const
// the user has specified. Here we place special strings that are
// almost certainly not going to collide with a file path or URL
// specified by the user, and placing them at the beginning of the
// string list so that they will be processed before the list of
// string listr so that they will be processed before the list of
// torrent paths or URLs.
if (!savePath.isEmpty())
@@ -403,9 +404,9 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
const QString &arg = args[i];
if ((arg.startsWith("--") && !arg.endsWith(".torrent"))
|| (arg.startsWith('-') && (arg.size() == 2))) {
|| (arg.startsWith("-") && (arg.size() == 2))) {
// Parse known parameters
if (arg == SHOW_HELP_OPTION) {
if ((arg == SHOW_HELP_OPTION)) {
result.showHelp = true;
}
#ifndef Q_OS_WIN
@@ -487,7 +488,7 @@ CommandLineParameterError::CommandLineParameterError(const QString &messageForUs
{
}
const QString &CommandLineParameterError::messageForUser() const
const QString& CommandLineParameterError::messageForUser() const
{
return m_messageForUser;
}
@@ -498,9 +499,9 @@ QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN
QStringList lines = {words.first()};
int currentLineMaxLength = wrapAtColumn - initialIndentation;
for (const QString &word : asConst(words.mid(1))) {
foreach (const QString &word, words.mid(1)) {
if (lines.last().length() + word.length() + 1 < currentLineMaxLength) {
lines.last().append(' ' + word);
lines.last().append(" " + word);
}
else {
lines.append(QString(initialIndentation, ' ') + word);
@@ -508,7 +509,7 @@ QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN
}
}
return lines.join('\n');
return lines.join("\n");
}
QString makeUsage(const QString &prgName)
@@ -577,7 +578,7 @@ QString makeUsage(const QString &prgName)
void displayUsage(const QString &prgName)
{
#ifndef Q_OS_WIN
printf("%s\n", qUtf8Printable(makeUsage(prgName)));
std::cout << qPrintable(makeUsage(prgName)) << std::endl;
#else
QMessageBox msgBox(QMessageBox::Information, QObject::tr("Help"), makeUsage(prgName), QMessageBox::Ok);
msgBox.show(); // Need to be shown or to moveToCenter does not work

View File

@@ -2,7 +2,7 @@
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2006 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -26,6 +26,8 @@
* 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.
*
* Contact : chris@qbittorrent.org
*/
#ifndef APP_OPTIONS_H
@@ -60,11 +62,11 @@ struct QBtCommandLineParameters
QStringList paramList() const;
};
class CommandLineParameterError : public std::runtime_error
class CommandLineParameterError: public std::runtime_error
{
public:
CommandLineParameterError(const QString &messageForUser);
const QString &messageForUser() const;
const QString& messageForUser() const;
private:
const QString m_messageForUser;

View File

@@ -26,14 +26,11 @@
* exception statement from your version.
*/
#include "filelogger.h"
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include "base/global.h"
#include "filelogger.h"
#include "base/logger.h"
#include "base/utils/fs.h"
@@ -44,17 +41,17 @@ FileLogger::FileLogger(const QString &path, const bool backup, const int maxSize
{
m_flusher.setInterval(0);
m_flusher.setSingleShot(true);
connect(&m_flusher, &QTimer::timeout, this, &FileLogger::flushLog);
connect(&m_flusher, SIGNAL(timeout()), SLOT(flushLog()));
changePath(path);
if (deleteOld)
this->deleteOld(age, ageType);
const Logger *const logger = Logger::instance();
for (const Log::Msg &msg : asConst(logger->getMessages()))
const Logger* const logger = Logger::instance();
foreach (const Log::Msg& msg, logger->getMessages())
addLogMessage(msg);
connect(logger, &Logger::newLogMessage, this, &FileLogger::addLogMessage);
connect(logger, SIGNAL(newLogMessage(const Log::Msg &)), SLOT(addLogMessage(const Log::Msg &)));
}
FileLogger::~FileLogger()
@@ -64,7 +61,7 @@ FileLogger::~FileLogger()
delete m_logFile;
}
void FileLogger::changePath(const QString &newPath)
void FileLogger::changePath(const QString& newPath)
{
QString tmpPath = Utils::Fs::fromNativePath(newPath);
QDir dir(tmpPath);
@@ -86,21 +83,21 @@ void FileLogger::changePath(const QString &newPath)
void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
{
QDateTime date = QDateTime::currentDateTime();
QDir dir(Utils::Fs::branchPath(m_path));
QDir dir(m_path);
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:
modificationDate = modificationDate.addDays(age);
break;
case MONTHS:
modificationDate = modificationDate.addMonths(age);
break;
default:
modificationDate = modificationDate.addYears(age);
}
if (modificationDate > date)
switch (ageType) {
case DAYS:
date = date.addDays(age);
break;
case MONTHS:
date = date.addMonths(age);
break;
default:
date = date.addYears(age);
}
foreach (const QFileInfo file, dir.entryInfoList(QStringList("qbittorrent.log.bak*"), QDir::Files | QDir::Writable, QDir::Time | QDir::Reversed)) {
if (file.lastModified() < date)
break;
Utils::Fs::forceRemove(file.absoluteFilePath());
}
@@ -168,7 +165,7 @@ void FileLogger::openLogFile()
|| !m_logFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner)) {
delete m_logFile;
m_logFile = nullptr;
Logger::instance()->addMessage(tr("An error occurred while trying to open the log file. Logging to file is disabled."), Log::CRITICAL);
Logger::instance()->addMessage(tr("An error occured while trying to open the log file. Logging to file is disabled."), Log::CRITICAL);
}
}

View File

@@ -76,3 +76,4 @@ private:
};
#endif // FILELOGGER_H

View File

@@ -1,7 +1,7 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2006 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -25,10 +25,10 @@
* 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.
*
* Contact : chris@qbittorrent.org
*/
#include <cstdlib>
#include <QDebug>
#include <QScopedPointer>
#include <QThread>
@@ -55,28 +55,33 @@ Q_IMPORT_PLUGIN(QICOPlugin)
#endif
#endif // DISABLE_GUI
#include <signal.h>
#ifdef STACKTRACE
#ifdef Q_OS_UNIX
#include <signal.h>
#include <execinfo.h>
#include "stacktrace.h"
#else
#include "stacktrace_win.h"
#include "stacktracedialog.h"
#endif // Q_OS_UNIX
#endif //STACKTRACE
#include "base/preferences.h"
#ifdef STACKTRACE_WIN
#include <signal.h>
#include "stacktrace_win.h"
#include "stacktrace_win_dlg.h"
#endif //STACKTRACE_WIN
#include <cstdlib>
#include <iostream>
#include "application.h"
#include "base/profile.h"
#include "base/utils/misc.h"
#include "application.h"
#include "base/preferences.h"
#include "cmdoptions.h"
#include "upgrade.h"
// Signal handlers
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
void sigNormalHandler(int signum);
#ifdef STACKTRACE
void sigAbnormalHandler(int signum);
#endif
// sys_signame[] is only defined in BSD
const char *sysSigName[] = {
#if defined(Q_OS_WIN)
@@ -91,9 +96,10 @@ const char *sysSigName[] = {
"SIGPWR", "SIGUNUSED"
#endif
};
#endif
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
void reportToUser(const char *str);
void reportToUser(const char* str);
#endif
void displayVersion();
@@ -102,6 +108,10 @@ void displayBadArgMessage(const QString &message);
#if !defined(DISABLE_GUI)
void showSplashScreen();
#if defined(Q_OS_UNIX)
void setupDpi();
#endif // Q_OS_UNIX
#endif // DISABLE_GUI
// Main
@@ -116,6 +126,10 @@ int main(int argc, char *argv[])
macMigratePlists();
#endif
#if !defined(DISABLE_GUI) && defined(Q_OS_UNIX)
setupDpi();
#endif
try {
// Create Application
QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString();
@@ -155,7 +169,7 @@ int main(int argc, char *argv[])
// Set environment variable
if (!qputenv("QBITTORRENT", QBT_VERSION))
fprintf(stderr, "Couldn't set environment variable...\n");
std::cerr << "Couldn't set environment variable...\n";
#ifndef DISABLE_GUI
if (!userAgreesWithLegalNotice())
@@ -202,7 +216,7 @@ int main(int argc, char *argv[])
// this is the default in Qt6
app->setAttribute(Qt::AA_DisableWindowContextHelpButton);
#endif
#endif // Q_OS_WIN
#endif
#if defined(Q_OS_MAC)
// Since Apple made difficult for users to set PATH, we set here for convenience.
@@ -226,7 +240,7 @@ int main(int argc, char *argv[])
#ifdef DISABLE_GUI
if (params.shouldDaemonize) {
app.reset(); // Destroy current application
if (daemon(1, 0) == 0) {
if ((daemon(1, 0) == 0)) {
app.reset(new Application(appId, argc, argv));
if (app->isRunning()) {
// Another instance had time to start.
@@ -243,9 +257,9 @@ int main(int argc, char *argv[])
showSplashScreen();
#endif
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
signal(SIGINT, sigNormalHandler);
signal(SIGTERM, sigNormalHandler);
#ifdef STACKTRACE
signal(SIGABRT, sigAbnormalHandler);
signal(SIGSEGV, sigAbnormalHandler);
#endif
@@ -259,7 +273,7 @@ int main(int argc, char *argv[])
}
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
void reportToUser(const char *str)
void reportToUser(const char* str)
{
const size_t strLen = strlen(str);
if (write(STDERR_FILENO, str, strLen) < static_cast<ssize_t>(strLen)) {
@@ -269,6 +283,7 @@ void reportToUser(const char *str)
}
#endif
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
void sigNormalHandler(int signum)
{
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
@@ -282,7 +297,6 @@ void sigNormalHandler(int signum)
qApp->exit(); // unsafe, but exit anyway
}
#ifdef STACKTRACE
void sigAbnormalHandler(int signum)
{
const char *sigName = sysSigName[signum];
@@ -295,41 +309,47 @@ void sigAbnormalHandler(int signum)
reportToUser(sigName);
reportToUser("\n");
print_stacktrace(); // unsafe
#endif
#if defined Q_OS_WIN
StacktraceDialog dlg; // unsafe
#endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU
#ifdef STACKTRACE_WIN
StraceDlg dlg; // unsafe
dlg.setStacktraceString(QLatin1String(sigName), straceWin::getBacktrace());
dlg.exec();
#endif
#endif // STACKTRACE_WIN
signal(signum, SIG_DFL);
raise(signum);
}
#endif // STACKTRACE
#endif // defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
#if !defined(DISABLE_GUI)
void showSplashScreen()
{
QPixmap splashImg(":/icons/skin/splash.png");
QPainter painter(&splashImg);
QPixmap splash_img(":/icons/skin/splash.png");
QPainter painter(&splash_img);
QString version = QBT_VERSION;
painter.setPen(QPen(Qt::white));
painter.setFont(QFont("Arial", 22, QFont::Black));
painter.drawText(224 - painter.fontMetrics().width(version), 270, version);
QSplashScreen *splash = new QSplashScreen(splashImg);
QSplashScreen *splash = new QSplashScreen(splash_img);
splash->show();
QTimer::singleShot(1500, splash, &QObject::deleteLater);
QTimer::singleShot(1500, splash, SLOT(deleteLater()));
qApp->processEvents();
}
#if defined(Q_OS_UNIX)
void setupDpi()
{
if (qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR").isEmpty())
qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1");
}
#endif // Q_OS_UNIX
#endif // DISABLE_GUI
void displayVersion()
{
printf("%s %s\n", qUtf8Printable(qApp->applicationName()), QBT_VERSION);
std::cout << qPrintable(qApp->applicationName()) << " " << QBT_VERSION << std::endl;
}
void displayBadArgMessage(const QString &message)
void displayBadArgMessage(const QString& message)
{
QString help = QObject::tr("Run application with -h option to read about command line parameters.");
#ifdef Q_OS_WIN
@@ -339,28 +359,24 @@ void displayBadArgMessage(const QString &message)
msgBox.move(Utils::Misc::screenCenter(&msgBox));
msgBox.exec();
#else
const QString errMsg = QObject::tr("Bad command line: ") + '\n'
+ message + '\n'
+ help + '\n';
fprintf(stderr, "%s", qUtf8Printable(errMsg));
std::cerr << qPrintable(QObject::tr("Bad command line: "));
std::cerr << qPrintable(message) << std::endl;
std::cerr << qPrintable(help) << std::endl;
#endif
}
bool userAgreesWithLegalNotice()
{
Preferences *const pref = Preferences::instance();
Preferences* const pref = Preferences::instance();
if (pref->getAcceptedLegal()) // Already accepted once
return true;
#ifdef DISABLE_GUI
const QString eula = QString("\n*** %1 ***\n").arg(QObject::tr("Legal Notice"))
+ QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.") + "\n\n"
+ QObject::tr("No further notices will be issued.") + "\n\n"
+ QObject::tr("Press %1 key to accept and continue...").arg("'y'") + '\n';
printf("%s", qUtf8Printable(eula));
std::cout << std::endl << "*** " << qPrintable(QObject::tr("Legal Notice")) << " ***" << std::endl;
std::cout << qPrintable(QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued.")) << std::endl << std::endl;
std::cout << qPrintable(QObject::tr("Press %1 key to accept and continue...").arg("'y'")) << std::endl;
char ret = getchar(); // Read pressed key
if ((ret == 'y') || (ret == 'Y')) {
if (ret == 'y' || ret == 'Y') {
// Save the answer
pref->setAcceptedLegal(true);
return true;
@@ -370,16 +386,16 @@ bool userAgreesWithLegalNotice()
msgBox.setText(QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued."));
msgBox.setWindowTitle(QObject::tr("Legal notice"));
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
QAbstractButton *agreeButton = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
QAbstractButton *agree_button = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
msgBox.show(); // Need to be shown or to moveToCenter does not work
msgBox.move(Utils::Misc::screenCenter(&msgBox));
msgBox.exec();
if (msgBox.clickedButton() == agreeButton) {
if (msgBox.clickedButton() == agree_button) {
// Save the answer
pref->setAcceptedLegal(true);
return true;
}
#endif // DISABLE_GUI
#endif
return false;
}

View File

@@ -8,24 +8,24 @@ set(QBT_QTSINGLEAPPLICATION_SOURCES
qtlocalpeer.cpp
)
if (Qt5Widgets_FOUND)
if (GUI)
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsingleapplication.h)
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsingleapplication.cpp)
else (Qt5Widgets_FOUND)
else (GUI)
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsinglecoreapplication.h)
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsinglecoreapplication.cpp)
endif (Qt5Widgets_FOUND)
endif (GUI)
add_library(qtsingleapplication STATIC ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}")
target_link_libraries(qtsingleapplication PRIVATE Qt5::Network)
target_link_qt_components(qtsingleapplication Network)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
target_compile_options(qtsingleapplication PRIVATE "-w") # disable warning for 3rdparty code
endif()
if (Qt5Widgets_FOUND)
target_link_libraries(qtsingleapplication PRIVATE Qt5::Widgets)
endif (Qt5Widgets_FOUND)
if (GUI)
target_link_qt_components(qtsingleapplication Widgets)
endif (GUI)
add_library(QtSingleApplication::QtSingleApplication ALIAS qtsingleapplication)

View File

@@ -138,7 +138,7 @@ bool straceWin::makeRelativePath(const QString& dir, QString& file)
QString straceWin::getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr)
{
IMAGEHLP_LINE64 line {};
IMAGEHLP_LINE64 line = {0};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD dwDisplacement = 0;
@@ -291,7 +291,7 @@ const QString straceWin::getBacktrace()
demangle(funcName);
#endif
// now ihsf.InstructionOffset points to the instruction that follows CALL instruction
// now ihsf.InstructionOffset points to the instruction that follows CALL instuction
// decrease the query address by one byte to point somewhere in the CALL instruction byte sequence
sourceFile = getSourcePathAndLineNumber(hProcess, ihsf.InstructionOffset - 1);
}

View File

@@ -27,21 +27,20 @@
*
*/
#ifndef STACKTRACEDIALOG_H
#define STACKTRACEDIALOG_H
#ifndef STACKTRACE_WIN_DLG_H
#define STACKTRACE_WIN_DLG_H
#include <QString>
#include <QDialog>
#include "base/utils/misc.h"
#include "ui_stacktracedialog.h"
#include "ui_stacktrace_win_dlg.h"
class StacktraceDialog : public QDialog, private Ui::StacktraceDialog
class StraceDlg : public QDialog, private Ui::errorDialog
{
Q_OBJECT
public:
StacktraceDialog(QWidget *parent = nullptr)
StraceDlg(QWidget *parent = nullptr)
: QDialog(parent)
{
setupUi(this);
@@ -84,4 +83,4 @@ public:
}
};
#endif // STACKTRACEDIALOG_H
#endif

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StacktraceDialog</class>
<widget class="QDialog" name="StacktraceDialog">
<class>errorDialog</class>
<widget class="QDialog" name="errorDialog">
<property name="geometry">
<rect>
<x>0</x>

View File

@@ -29,43 +29,42 @@
#ifndef UPGRADE_H
#define UPGRADE_H
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/version.hpp>
#if LIBTORRENT_VERSION_NUM >= 10100
#include <libtorrent/bdecode.hpp>
#else
#endif
#include <libtorrent/bencode.hpp>
#include <libtorrent/entry.hpp>
#if LIBTORRENT_VERSION_NUM < 10100
#include <libtorrent/lazy_entry.hpp>
#endif
#include <QDir>
#include <QFile>
#include <QRegularExpression>
#include <QString>
#ifndef DISABLE_GUI
#include <QMessageBox>
#endif
#include <QRegExp>
#include <QString>
#ifdef Q_OS_MAC
#include <QSettings>
#endif
#include "base/logger.h"
#include "base/preferences.h"
#include "base/profile.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "base/preferences.h"
bool userAcceptsUpgrade()
{
#ifdef DISABLE_GUI
printf("\n*** %s ***\n", qUtf8Printable(QObject::tr("Upgrade")));
std::cout << std::endl << "*** " << qPrintable(QObject::tr("Upgrade")) << " ***" << std::endl;
char ret = '\0';
do {
printf("%s\n"
, qUtf8Printable(QObject::tr("You updated from an older version that saved things differently. You must migrate to the new saving system. You will not be able to use an older version than v3.3.0 again. Continue? [y/n]")));
std::cout << qPrintable(QObject::tr("You updated from an older version that saved things differently. You must migrate to the new saving system. You will not be able to use an older version than v3.3.0 again. Continue? [y/n]")) << std::endl;
ret = getchar(); // Read pressed key
}
while ((ret != 'y') && (ret != 'Y') && (ret != 'n') && (ret != 'N'));
@@ -83,7 +82,7 @@ bool userAcceptsUpgrade()
msgBox.move(Utils::Misc::screenCenter(&msgBox));
if (msgBox.exec() == QMessageBox::Ok)
return true;
#endif // DISABLE_GUI
#endif
return false;
}
@@ -114,28 +113,12 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent =
bool v3_3 = false;
int queuePosition = 0;
QString outFilePath = filepath;
static const QRegularExpression rx(QLatin1String("([A-Fa-f0-9]{40})\\.fastresume\\.(.+)$"));
const QRegularExpressionMatch rxMatch = rx.match(filepath);
if (rxMatch.hasMatch()) {
// Old v3.3.x format had a number at the end indicating the queue position.
// The naming scheme was '<infohash>.fastresume.<queueposition>'.
// However, QSaveFile, which uses QTemporaryFile internally, might leave
// non-commited files behind eg after a crash. These files have the
// naming scheme '<infohash>.fastresume.XXXXXX' where each X is a random
// character. So we detect if the last part is present. Then check if it
// is 6 chars long. If all the 6 chars are digits we assume it is an old
// v3.3.x format. Otherwise it is considered a non-commited fastresume
// and is deleted, because it may be a corrupted/incomplete fastresume.
// NOTE: When the upgrade code is removed, we must continue to perform
// cleanup of non-commited QSaveFile/QTemporaryFile fastresumes
queuePosition = rxMatch.captured(2).toInt();
if ((rxMatch.captured(2).size() == 6) && (queuePosition <= 99999)) {
Utils::Fs::forceRemove(filepath);
return true;
}
QRegExp rx(QLatin1String("([A-Fa-f0-9]{40})\\.fastresume\\.(\\d+)$"));
if (rx.indexIn(filepath) != -1) {
// old v3.3.x format
queuePosition = rx.cap(2).toInt();
v3_3 = true;
outFilePath.replace(QRegularExpression("\\.fastresume\\..+$"), ".fastresume");
outFilePath.replace(QRegExp("\\.\\d+$"), "");
}
else {
queuePosition = fastOld.dict_find_int_value("qBt-queuePosition", 0);
@@ -146,15 +129,6 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent =
// in versions < 3.3 we have -1 for seeding torrents, so we convert it to 0
fastNew["qBt-queuePosition"] = (queuePosition >= 0 ? queuePosition : 0);
if (v3_3) {
QFileInfo oldFile(filepath);
QFileInfo newFile(outFilePath);
if (newFile.exists()
&& (oldFile.lastModified() < newFile.lastModified())) {
Utils::Fs::forceRemove(filepath);
return true;
}
}
QFile file2(outFilePath);
QVector<char> out;
libtorrent::bencode(std::back_inserter(out), fastNew);
@@ -179,9 +153,9 @@ bool upgrade(bool ask = true)
// ****************************************************************************************
// Silently converts old v3.3.x .fastresume files
const QStringList backupFiles_3_3 = backupFolderDir.entryList(
QStringList backupFiles_3_3 = backupFolderDir.entryList(
QStringList(QLatin1String("*.fastresume.*")), QDir::Files, QDir::Unsorted);
for (const QString &backupFile : backupFiles_3_3)
foreach (const QString &backupFile, backupFiles_3_3)
upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile));
// ****************************************************************************************
@@ -197,27 +171,24 @@ bool upgrade(bool ask = true)
if (ask && !userAcceptsUpgrade()) return false;
const QStringList backupFiles = backupFolderDir.entryList(
QStringList backupFiles = backupFolderDir.entryList(
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
const QRegularExpression rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
for (const QString &backupFile : backupFiles) {
const QRegularExpressionMatch rxMatch = rx.match(backupFile);
if (rxMatch.hasMatch()) {
const QString hashStr = rxMatch.captured(1);
if (upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile), oldResumeData[hashStr].toHash()))
oldResumeData.remove(hashStr);
QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
foreach (QString backupFile, backupFiles) {
if (rx.indexIn(backupFile) != -1) {
if (upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile), oldResumeData[rx.cap(1)].toHash()))
oldResumeData.remove(rx.cap(1));
else
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent with hash: %1").arg(hashStr), Log::WARNING);
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent with hash: %1").arg(rx.cap(1)), Log::WARNING);
}
else {
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent. Invalid fastresume file name: %1").arg(backupFile), Log::WARNING);
}
}
for (auto i = oldResumeData.cbegin(); i != oldResumeData.cend(); ++i) {
const QVariantHash oldTorrent = i.value().toHash();
foreach (const QString &hash, oldResumeData.keys()) {
QVariantHash oldTorrent = oldResumeData[hash].toHash();
if (oldTorrent.value("is_magnet", false).toBool()) {
const QString &hash = i.key();
libtorrent::entry resumeData;
resumeData["qBt-magnetUri"] = oldTorrent.value("magnet_uri").toString().toStdString();
resumeData["qBt-paused"] = false;
@@ -265,7 +236,7 @@ void migratePlistToIni(const QString &application)
plistFile->setFallbacksEnabled(false);
const QStringList plist = plistFile->allKeys();
if (!plist.isEmpty()) {
for (const QString &key : plist)
foreach (const QString &key, plist)
iniFile.setValue(key, plistFile->value(key));
plistFile->clear();
}
@@ -299,6 +270,6 @@ void migrateRSS()
qBTRSSLegacy->remove("old_items");
}
}
#endif // DISABLE_GUI
#endif
#endif // UPGRADE_H

View File

@@ -1,10 +1,8 @@
find_package(ZLIB 1.2.5.2 REQUIRED)
add_library(qbt_base STATIC
# headers
set(QBT_BASE_HEADERS
bittorrent/addtorrentparams.h
bittorrent/cachestatus.h
bittorrent/filepriority.h
bittorrent/infohash.h
bittorrent/magneturi.h
bittorrent/peerinfo.h
@@ -21,7 +19,6 @@ bittorrent/torrentinfo.h
bittorrent/tracker.h
bittorrent/trackerentry.h
http/connection.h
http/httperror.h
http/irequesthandler.h
http/requestparser.h
http/responsebuilder.h
@@ -46,11 +43,6 @@ rss/rss_feed.h
rss/rss_folder.h
rss/rss_item.h
rss/rss_session.h
search/searchdownloadhandler.h
search/searchhandler.h
search/searchpluginmanager.h
utils/bytearray.h
utils/foreignapps.h
utils/fs.h
utils/gzip.h
utils/misc.h
@@ -58,9 +50,7 @@ utils/net.h
utils/random.h
utils/string.h
utils/version.h
algorithm.h
asyncfilestorage.h
exceptions.h
filesystemwatcher.h
global.h
iconprovider.h
@@ -69,15 +59,16 @@ logger.h
preferences.h
profile.h
scanfoldersmodel.h
searchengine.h
settingsstorage.h
torrentfileguard.h
torrentfilter.h
tristatebool.h
types.h
unicodestrings.h
)
# sources
bittorrent/filepriority.cpp
set(QBT_BASE_SOURCES
bittorrent/infohash.cpp
bittorrent/magneturi.cpp
bittorrent/peerinfo.cpp
@@ -93,7 +84,6 @@ bittorrent/torrentinfo.cpp
bittorrent/tracker.cpp
bittorrent/trackerentry.cpp
http/connection.cpp
http/httperror.cpp
http/requestparser.cpp
http/responsebuilder.cpp
http/responsegenerator.cpp
@@ -116,11 +106,6 @@ rss/rss_feed.cpp
rss/rss_folder.cpp
rss/rss_item.cpp
rss/rss_session.cpp
search/searchdownloadhandler.cpp
search/searchhandler.cpp
search/searchpluginmanager.cpp
utils/bytearray.cpp
utils/foreignapps.cpp
utils/fs.cpp
utils/gzip.cpp
utils/misc.cpp
@@ -128,33 +113,29 @@ utils/net.cpp
utils/random.cpp
utils/string.cpp
asyncfilestorage.cpp
exceptions.cpp
filesystemwatcher.cpp
iconprovider.cpp
logger.cpp
preferences.cpp
profile.cpp
scanfoldersmodel.cpp
searchengine.cpp
settingsstorage.cpp
torrentfileguard.cpp
torrentfilter.cpp
tristatebool.cpp
)
target_link_libraries(qbt_base
PRIVATE
ZLIB::ZLIB
PUBLIC
LibtorrentRasterbar::torrent-rasterbar
Qt5::Core Qt5::Network Qt5::Xml
)
add_library(qbt_base STATIC ${QBT_BASE_HEADERS} ${QBT_BASE_SOURCES})
target_link_libraries(qbt_base PRIVATE ZLIB::ZLIB PUBLIC LibtorrentRasterbar::LibTorrent)
target_link_qt_components(qbt_base PUBLIC Core Network Xml)
if (Qt5Widgets_FOUND)
if (GUI)
target_link_libraries(qbt_base PUBLIC Qt5::Gui Qt5::Widgets)
endif (Qt5Widgets_FOUND)
endif (GUI)
if (Qt5DBus_FOUND)
target_link_libraries(qbt_base PRIVATE Qt5::DBus)
if (DBUS)
target_link_qt_components(qbt_base PRIVATE DBus)
endif ()
if (APPLE)

View File

@@ -1,41 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* 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 Dict
{
// To be used with QMap, QHash and it's variants
template <typename Dictionary, typename BinaryPredicate>
void removeIf(Dictionary &&dict, BinaryPredicate p)
{
auto it = dict.begin();
while (it != dict.end())
it = (p(it.key(), it.value()) ? dict.erase(it) : it + 1);
}
}

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 {tr("Could not create directory '%1'.")
.arg(m_storageDir.absolutePath())};
throw AsyncFileStorageError(
QString("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,3 +76,13 @@ 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>
#include "base/exceptions.h"
class AsyncFileStorageError : public RuntimeError
class AsyncFileStorageError: public std::runtime_error
{
public:
using RuntimeError::RuntimeError;
explicit AsyncFileStorageError(const QString &message);
QString message() const;
};
class AsyncFileStorage : public QObject
class AsyncFileStorage: public QObject
{
Q_OBJECT
Q_DISABLE_COPY(AsyncFileStorage)
public:
explicit AsyncFileStorage(const QString &storageFolderPath, QObject *parent = nullptr);

View File

@@ -1,9 +1,7 @@
HEADERS += \
$$PWD/algorithm.h \
$$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 \
@@ -19,11 +17,9 @@ HEADERS += \
$$PWD/bittorrent/torrentinfo.h \
$$PWD/bittorrent/tracker.h \
$$PWD/bittorrent/trackerentry.h \
$$PWD/exceptions.h \
$$PWD/filesystemwatcher.h \
$$PWD/global.h \
$$PWD/http/connection.h \
$$PWD/http/httperror.h \
$$PWD/http/irequesthandler.h \
$$PWD/http/requestparser.h \
$$PWD/http/responsebuilder.h \
@@ -54,9 +50,7 @@ HEADERS += \
$$PWD/rss/rss_item.h \
$$PWD/rss/rss_session.h \
$$PWD/scanfoldersmodel.h \
$$PWD/search/searchhandler.h \
$$PWD/search/searchdownloadhandler.h \
$$PWD/search/searchpluginmanager.h \
$$PWD/searchengine.h \
$$PWD/settingsstorage.h \
$$PWD/settingvalue.h \
$$PWD/torrentfileguard.h \
@@ -64,8 +58,6 @@ HEADERS += \
$$PWD/tristatebool.h \
$$PWD/types.h \
$$PWD/unicodestrings.h \
$$PWD/utils/bytearray.h \
$$PWD/utils/foreignapps.h \
$$PWD/utils/fs.h \
$$PWD/utils/gzip.h \
$$PWD/utils/misc.h \
@@ -76,7 +68,6 @@ HEADERS += \
SOURCES += \
$$PWD/asyncfilestorage.cpp \
$$PWD/bittorrent/filepriority.cpp \
$$PWD/bittorrent/infohash.cpp \
$$PWD/bittorrent/magneturi.cpp \
$$PWD/bittorrent/peerinfo.cpp \
@@ -91,10 +82,8 @@ SOURCES += \
$$PWD/bittorrent/torrentinfo.cpp \
$$PWD/bittorrent/tracker.cpp \
$$PWD/bittorrent/trackerentry.cpp \
$$PWD/exceptions.cpp \
$$PWD/filesystemwatcher.cpp \
$$PWD/http/connection.cpp \
$$PWD/http/httperror.cpp \
$$PWD/http/requestparser.cpp \
$$PWD/http/responsebuilder.cpp \
$$PWD/http/responsegenerator.cpp \
@@ -122,15 +111,11 @@ SOURCES += \
$$PWD/rss/rss_item.cpp \
$$PWD/rss/rss_session.cpp \
$$PWD/scanfoldersmodel.cpp \
$$PWD/search/searchdownloadhandler.cpp \
$$PWD/search/searchhandler.cpp \
$$PWD/search/searchpluginmanager.cpp \
$$PWD/searchengine.cpp \
$$PWD/settingsstorage.cpp \
$$PWD/torrentfileguard.cpp \
$$PWD/torrentfilter.cpp \
$$PWD/tristatebool.cpp \
$$PWD/utils/bytearray.cpp \
$$PWD/utils/foreignapps.cpp \
$$PWD/utils/fs.cpp \
$$PWD/utils/gzip.cpp \
$$PWD/utils/misc.cpp \

View File

@@ -1,46 +0,0 @@
/*
* 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

@@ -1,43 +0,0 @@
/*
* 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

@@ -26,9 +26,8 @@
* exception statement from your version.
*/
#include "infohash.h"
#include <QHash>
#include "infohash.h"
using namespace BitTorrent;
@@ -92,7 +91,7 @@ bool InfoHash::operator!=(const InfoHash &other) const
return (m_nativeHash != other.m_nativeHash);
}
uint BitTorrent::qHash(const InfoHash &key, uint seed)
uint qHash(const InfoHash &key, uint seed)
{
return qHash(static_cast<QString>(key), seed);
}

View File

@@ -29,8 +29,8 @@
#ifndef BITTORRENT_INFOHASH_H
#define BITTORRENT_INFOHASH_H
#include <libtorrent/sha1_hash.hpp>
#include <QString>
#include <libtorrent/sha1_hash.hpp>
namespace BitTorrent
{
@@ -54,8 +54,8 @@ namespace BitTorrent
libtorrent::sha1_hash m_nativeHash;
QString m_hashString;
};
uint qHash(const InfoHash &key, uint seed);
}
uint qHash(const BitTorrent::InfoHash &key, uint seed);
#endif // BITTORRENT_INFOHASH_H

View File

@@ -26,17 +26,16 @@
* exception statement from your version.
*/
#include "magneturi.h"
#include <QByteArray>
#include <QRegExp>
#include <QStringList>
#include <libtorrent/bencode.hpp>
#include <libtorrent/error_code.hpp>
#include <libtorrent/magnet_uri.hpp>
#include <QByteArray>
#include <QRegularExpression>
#include <QStringList>
#include "base/utils/string.h"
#include "magneturi.h"
namespace
{
@@ -46,7 +45,7 @@ namespace
rawBc = rawBc.mid(8); // skip bc://bt/
rawBc = QByteArray::fromBase64(rawBc); // Decode base64
// Format is now AA/url_encoded_filename/size_bytes/info_hash/ZZ
QStringList parts = QString(rawBc).split('/');
QStringList parts = QString(rawBc).split("/");
if (parts.size() != 5) return QString();
QString filename = parts.at(1);
@@ -70,8 +69,8 @@ MagnetUri::MagnetUri(const QString &source)
qDebug("Creating magnet link from bc link");
m_url = bcLinkToMagnet(source);
}
else if (((source.size() == 40) && !source.contains(QRegularExpression("[^0-9A-Fa-f]")))
|| ((source.size() == 32) && !source.contains(QRegularExpression("[^2-7A-Za-z]")))) {
else if (((source.size() == 40) && !source.contains(QRegExp("[^0-9A-Fa-f]")))
|| ((source.size() == 32) && !source.contains(QRegExp("[^2-7A-Za-z]")))) {
m_url = "magnet:?xt=urn:btih:" + source;
}
@@ -83,10 +82,10 @@ MagnetUri::MagnetUri(const QString &source)
m_hash = m_addTorrentParams.info_hash;
m_name = QString::fromStdString(m_addTorrentParams.name);
for (const std::string &tracker : m_addTorrentParams.trackers)
foreach (const std::string &tracker, m_addTorrentParams.trackers)
m_trackers.append(QString::fromStdString(tracker));
for (const std::string &urlSeed : m_addTorrentParams.url_seeds)
foreach (const std::string &urlSeed, m_addTorrentParams.url_seeds)
m_urlSeeds.append(QUrl(urlSeed.c_str()));
}

View File

@@ -246,8 +246,8 @@ QString PeerInfo::connectionType() const
void PeerInfo::calcRelevance(const TorrentHandle *torrent)
{
const QBitArray allPieces = torrent->pieces();
const QBitArray peerPieces = pieces();
const QBitArray &allPieces = torrent->pieces();
const QBitArray &peerPieces = pieces();
int localMissing = 0;
int remoteHaves = 0;
@@ -377,7 +377,7 @@ void PeerInfo::determineFlags()
// L = Peer is local
if (fromLSD()) {
m_flags += 'L';
m_flags += "L";
flagsDescriptionList += "L = "
+ tr("peer from LSD");
}

View File

@@ -55,7 +55,7 @@ void BandwidthScheduler::start()
bool BandwidthScheduler::isTimeForAlternative() const
{
const Preferences *const pref = Preferences::instance();
const Preferences* const pref = Preferences::instance();
QTime start = pref->getSchedulerStartTime();
QTime end = pref->getSchedulerEndTime();

View File

@@ -33,7 +33,7 @@
#include <QObject>
#include <QTimer>
class BandwidthScheduler : public QObject
class BandwidthScheduler: public QObject
{
Q_OBJECT
Q_DISABLE_COPY(BandwidthScheduler)

View File

@@ -1,5 +1,5 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Bittorrent Client using Qt and libt.
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@@ -147,13 +147,13 @@ int FilterParserThread::parseDATFilterFile()
if (bytesRead < 0)
break;
int dataSize = bytesRead + offset;
if ((bytesRead == 0) && (dataSize == 0))
if (bytesRead == 0 && dataSize == 0)
break;
for (start = 0; start < dataSize; ++start) {
endOfLine = -1;
// The file might have ended without the last line having a newline
if (!((bytesRead == 0) && (dataSize > 0))) {
if (!(bytesRead == 0 && dataSize > 0)) {
for (int i = start; i < dataSize; ++i) {
if (buffer[i] == '\n') {
endOfLine = i;
@@ -295,13 +295,13 @@ int FilterParserThread::parseP2PFilterFile()
if (bytesRead < 0)
break;
int dataSize = bytesRead + offset;
if ((bytesRead == 0) && (dataSize == 0))
if (bytesRead == 0 && dataSize == 0)
break;
for (start = 0; start < dataSize; ++start) {
endOfLine = -1;
// The file might have ended without the last line having a newline
if (!((bytesRead == 0) && (dataSize > 0))) {
if (!(bytesRead == 0 && dataSize > 0)) {
for (int i = start; i < dataSize; ++i) {
if (buffer[i] == '\n') {
endOfLine = i;
@@ -407,24 +407,24 @@ int FilterParserThread::parseP2PFilterFile()
int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name, char delim)
{
char c;
int totalRead = 0;
int total_read = 0;
int read;
do {
read = stream.readRawData(&c, 1);
totalRead += read;
total_read += read;
if (read > 0) {
if (c != delim) {
name += c;
}
else {
// Delim found
return totalRead;
return total_read;
}
}
}
while (read > 0);
while(read > 0);
return totalRead;
return total_read;
}
// Parser for PeerGuardian ip filter in p2p format
@@ -455,7 +455,7 @@ int FilterParserThread::parseP2BFilterFile()
unsigned int start, end;
std::string name;
while (getlineInStream(stream, name, '\0') && !m_abort) {
while(getlineInStream(stream, name, '\0') && !m_abort) {
if (!stream.readRawData(reinterpret_cast<char*>(&start), sizeof(start))
|| !stream.readRawData(reinterpret_cast<char*>(&end), sizeof(end))) {
LogMsg(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
@@ -610,7 +610,7 @@ int FilterParserThread::findAndNullDelimiter(char *const data, char delimiter, i
return -1;
}
int FilterParserThread::trim(char *const data, int start, int end)
int FilterParserThread::trim(char* const data, int start, int end)
{
if (start >= end) return start;
int newStart = start;

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015 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,36 +26,30 @@
* exception statement from your version.
*/
#include "resumedatasavingmanager.h"
#include <QDebug>
#include <QSaveFile>
#include "base/logger.h"
#include "base/utils/fs.h"
#include "resumedatasavingmanager.h"
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
: m_resumeDataDir(resumeFolderPath)
{
}
void ResumeDataSavingManager::save(const QString &filename, const QByteArray &data) const
void ResumeDataSavingManager::saveResumeData(QString infoHash, QByteArray data) const
{
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
QString filename = QString("%1.fastresume").arg(infoHash);
QString filepath = m_resumeDataDir.absoluteFilePath(filename);
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);
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).arg(resumeFile.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, 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015 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,7 +26,8 @@
* exception statement from your version.
*/
#pragma once
#ifndef RESUMEDATASAVINGMANAGER_H
#define RESUMEDATASAVINGMANAGER_H
#include <QByteArray>
#include <QDir>
@@ -35,15 +36,15 @@
class ResumeDataSavingManager : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(ResumeDataSavingManager)
public:
explicit ResumeDataSavingManager(const QString &resumeFolderPath);
public slots:
void save(const QString &filename, const QByteArray &data) const;
void remove(const QString &filename) const;
void saveResumeData(QString infoHash, QByteArray data) const;
private:
QDir m_resumeDataDir;
};
#endif // RESUMEDATASAVINGMANAGER_H

View File

@@ -22,7 +22,7 @@ Statistics::Statistics(Session *session)
, m_dirty(false)
{
load();
connect(&m_timer, &QTimer::timeout, this, &Statistics::gather);
connect(&m_timer, SIGNAL(timeout()), this, SLOT(gather()));
m_timer.start(60 * 1000);
}

View File

@@ -9,7 +9,7 @@ namespace BitTorrent
class Session;
}
class Statistics : public QObject
class Statistics : QObject
{
Q_OBJECT
Q_DISABLE_COPY(Statistics)
@@ -28,6 +28,7 @@ private:
void save() const;
void load();
private:
BitTorrent::Session *m_session;
// Will overflow at 15.9 EiB
quint64 m_alltimeUL;

File diff suppressed because it is too large Load Diff

View File

@@ -30,14 +30,18 @@
#ifndef BITTORRENT_SESSION_H
#define BITTORRENT_SESSION_H
#include <vector>
#include <libtorrent/version.hpp>
#include <vector>
#if LIBTORRENT_VERSION_NUM >= 10100
#include <QElapsedTimer>
#endif
#include <QFile>
#include <QHash>
#include <QList>
#include <QMap>
#if LIBTORRENT_VERSION_NUM < 10100
#include <QMutex>
#endif
#include <QNetworkConfigurationManager>
#include <QPointer>
#include <QSet>
@@ -45,12 +49,6 @@
#include <QVector>
#include <QWaitCondition>
#if LIBTORRENT_VERSION_NUM < 10100
#include <QMutex>
#else
#include <QElapsedTimer>
#endif
#include "base/settingvalue.h"
#include "base/tristatebool.h"
#include "base/types.h"
@@ -113,6 +111,7 @@ class QTimer;
class QStringList;
class QString;
class QUrl;
template<typename T> class QList;
class FilterParserThread;
class BandwidthScheduler;
@@ -138,7 +137,7 @@ namespace BitTorrent
class Tracker;
class MagnetUri;
class TrackerEntry;
struct CreateTorrentParams;
struct AddTorrentData;
struct TorrentStatusReport
{
@@ -237,7 +236,7 @@ namespace BitTorrent
int diskJobTime = 0;
} disk;
};
#endif // LIBTORRENT_VERSION_NUM >= 10100
#endif
class Session : public QObject
{
@@ -375,10 +374,6 @@ namespace BitTorrent
void setAnnounceToAllTrackers(bool val);
bool announceToAllTiers() const;
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;
@@ -387,8 +382,6 @@ namespace BitTorrent
void setUseOSCache(bool use);
bool isGuidedReadCacheEnabled() const;
void setGuidedReadCacheEnabled(bool enabled);
bool isCoalesceReadWriteEnabled() const;
void setCoalesceReadWriteEnabled(bool enabled);
bool isSuggestModeEnabled() const;
void setSuggestMode(bool mode);
int sendBufferWatermark() const;
@@ -403,12 +396,6 @@ namespace BitTorrent
void setQueueingSystemEnabled(bool enabled);
bool ignoreSlowTorrentsForQueueing() const;
void setIgnoreSlowTorrentsForQueueing(bool ignore);
int downloadRateForSlowTorrents() const;
void setDownloadRateForSlowTorrents(int rateInKibiBytes);
int uploadRateForSlowTorrents() const;
void setUploadRateForSlowTorrents(int rateInKibiBytes);
int slowTorrentsInactivityTimer() const;
void setSlowTorrentsInactivityTimer(int timeInSeconds);
int outgoingPortsMin() const;
void setOutgoingPortsMin(int min);
int outgoingPortsMax() const;
@@ -456,7 +443,6 @@ namespace BitTorrent
TorrentStatusReport torrentStatusReport() const;
bool hasActiveTorrents() const;
bool hasUnfinishedTorrents() const;
bool hasRunningSeed() const;
const SessionStatus &status() const;
const CacheStatus &cacheStatus() const;
quint64 getAlltimeDL() const;
@@ -483,7 +469,6 @@ namespace BitTorrent
// TorrentHandle interface
void handleTorrentShareLimitChanged(TorrentHandle *const torrent);
void handleTorrentNameChanged(TorrentHandle *const torrent);
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
void handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag);
@@ -553,7 +538,7 @@ namespace BitTorrent
void generateResumeData(bool final = false);
void handleIPFilterParsed(int ruleCount);
void handleIPFilterError();
void handleDownloadFinished(const QString &url, const QByteArray &data);
void handleDownloadFinished(const QString &url, const QString &filePath);
void handleDownloadFailed(const QString &url, const QString &reason);
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
@@ -569,7 +554,7 @@ namespace BitTorrent
bool requestedFileDeletion;
};
explicit Session(QObject *parent = nullptr);
explicit Session(QObject *parent = 0);
~Session();
bool hasPerTorrentRatioLimit() const;
@@ -601,14 +586,14 @@ namespace BitTorrent
void enableIPFilter();
void disableIPFilter();
bool addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri,
bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
TorrentInfo torrentInfo = TorrentInfo(),
const QByteArray &fastresumeData = QByteArray());
bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
void updateSeedingLimitTimer();
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
void saveTorrentResumeData(TorrentHandle *const torrent);
void saveTorrentResumeData(TorrentHandle *const torrent, bool finalSave = false);
void handleAlert(libtorrent::alert *a);
void dispatchTorrentAlert(libtorrent::alert *a);
@@ -634,8 +619,6 @@ 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);
@@ -659,13 +642,10 @@ namespace BitTorrent
CachedSettingValue<QString> m_IPFilterFile;
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;
CachedSettingValue<bool> m_guidedReadCacheEnabled;
CachedSettingValue<bool> m_coalesceReadWriteEnabled;
CachedSettingValue<bool> m_isSuggestMode;
CachedSettingValue<int> m_sendBufferWatermark;
CachedSettingValue<int> m_sendBufferLowWatermark;
@@ -676,9 +656,6 @@ namespace BitTorrent
CachedSettingValue<int> m_maxActiveUploads;
CachedSettingValue<int> m_maxActiveTorrents;
CachedSettingValue<bool> m_ignoreSlowTorrentsForQueueing;
CachedSettingValue<int> m_downloadRateForSlowTorrents;
CachedSettingValue<int> m_uploadRateForSlowTorrents;
CachedSettingValue<int> m_slowTorrentsInactivityTimer;
CachedSettingValue<int> m_outgoingPortsMin;
CachedSettingValue<int> m_outgoingPortsMax;
CachedSettingValue<bool> m_ignoreLimitsOnLAN;
@@ -764,17 +741,13 @@ namespace BitTorrent
QHash<InfoHash, TorrentInfo> m_loadedMetadata;
QHash<InfoHash, TorrentHandle *> m_torrents;
QHash<InfoHash, CreateTorrentParams> m_addingTorrents;
QHash<InfoHash, AddTorrentData> m_addingTorrents;
QHash<QString, AddTorrentParams> m_downloadedTorrents;
QHash<InfoHash, RemovingTorrentData> m_removingTorrents;
TorrentStatusReport m_torrentStatusReport;
QStringMap m_categories;
QSet<QString> m_tags;
// I/O errored torrents
QSet<InfoHash> m_recentErroredTorrents;
QTimer *m_recentErroredTorrentsTimer;
#if LIBTORRENT_VERSION_NUM < 10100
QMutex m_alertsMutex;
QWaitCondition m_alertsWaitCondition;

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2010 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,6 +24,8 @@
* 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.
*
* Contact : chris@qbittorrent.org
*/
#include "torrentcreatorthread.h"
@@ -35,14 +37,9 @@
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/storage.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/version.hpp>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QHash>
#include "base/global.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/string.h"
@@ -89,66 +86,23 @@ void TorrentCreatorThread::run()
emit updateProgress(0);
try {
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + '/';
// Adding files to the torrent
libt::file_storage fs;
if (QFileInfo(m_params.inputPath).isFile()) {
libt::add_files(fs, Utils::Fs::toNativePath(m_params.inputPath).toStdString(), fileFilter);
}
else {
// need to sort the file names by natural sort order
QStringList dirs = {m_params.inputPath};
QDirIterator dirIter(m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories);
while (dirIter.hasNext()) {
dirIter.next();
dirs += dirIter.filePath();
}
std::sort(dirs.begin(), dirs.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
QStringList fileNames;
QHash<QString, boost::int64_t> fileSizeMap;
for (const auto &dir : asConst(dirs)) {
QStringList tmpNames; // natural sort files within each dir
QDirIterator fileIter(dir, QDir::Files);
while (fileIter.hasNext()) {
fileIter.next();
const QString relFilePath = fileIter.filePath().mid(parentPath.length());
tmpNames += relFilePath;
fileSizeMap[relFilePath] = fileIter.fileInfo().size();
}
std::sort(tmpNames.begin(), tmpNames.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
fileNames += tmpNames;
}
for (const auto &fileName : asConst(fileNames))
fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
}
// Adding files to the torrent
libt::add_files(fs, Utils::Fs::toNativePath(m_params.inputPath).toStdString(), fileFilter);
if (isInterruptionRequested()) return;
#if LIBTORRENT_VERSION_NUM < 10100
libt::create_torrent newTorrent(fs, m_params.pieceSize, -1
, (m_params.isAlignmentOptimized ? libt::create_torrent::optimize : 0));
#else
libt::create_torrent newTorrent(fs, m_params.pieceSize, -1
, (m_params.isAlignmentOptimized ? libt::create_torrent::optimize_alignment : 0));
#endif
libt::create_torrent newTorrent(fs, m_params.pieceSize);
// Add url seeds
for (QString seed : asConst(m_params.urlSeeds)) {
foreach (QString seed, m_params.urlSeeds) {
seed = seed.trimmed();
if (!seed.isEmpty())
newTorrent.add_url_seed(seed.toStdString());
}
int tier = 0;
for (const QString &tracker : asConst(m_params.trackers)) {
foreach (const QString &tracker, m_params.trackers) {
if (tracker.isEmpty())
++tier;
else
@@ -158,6 +112,7 @@ void TorrentCreatorThread::run()
if (isInterruptionRequested()) return;
// calculate the hash for all pieces
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + "/";
libt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
, [this, &newTorrent](const int n) { sendProgressSignal(n, newTorrent.num_pieces()); });
// Set qBittorrent as creator and add user comment to
@@ -201,19 +156,12 @@ void TorrentCreatorThread::run()
}
}
int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize, const bool isAlignmentOptimized)
int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize)
{
if (inputPath.isEmpty())
return 0;
libt::file_storage fs;
libt::add_files(fs, Utils::Fs::toNativePath(inputPath).toStdString(), fileFilter);
#if LIBTORRENT_VERSION_NUM < 10100
return libt::create_torrent(fs, pieceSize, -1
, (isAlignmentOptimized ? libt::create_torrent::optimize : 0)).num_pieces();
#else
return libt::create_torrent(fs, pieceSize, -1
, (isAlignmentOptimized ? libt::create_torrent::optimize_alignment : 0)).num_pieces();
#endif
return libt::create_torrent(fs, pieceSize).num_pieces();
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2010 Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,6 +24,8 @@
* 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.
*
* Contact : chris@qbittorrent.org
*/
#ifndef BITTORRENT_TORRENTCREATORTHREAD_H
@@ -37,7 +39,6 @@ namespace BitTorrent
struct TorrentCreatorParams
{
bool isPrivate;
bool isAlignmentOptimized;
int pieceSize;
QString inputPath;
QString savePath;
@@ -57,7 +58,7 @@ namespace BitTorrent
void create(const TorrentCreatorParams &params);
static int calculateTotalPieces(const QString &inputPath, const int pieceSize, const bool isAlignmentOptimized);
static int calculateTotalPieces(const QString &inputPath, const int pieceSize);
protected:
void run();

File diff suppressed because it is too large Load Diff

View File

@@ -88,10 +88,10 @@ namespace BitTorrent
class TrackerEntry;
struct AddTorrentParams;
struct CreateTorrentParams
struct AddTorrentData
{
bool restored; // is existing torrent job?
// for both new and restored torrents
bool resumed;
// for both new and resumed torrents
QString name;
QString category;
QSet<QString> tags;
@@ -102,18 +102,18 @@ namespace BitTorrent
bool hasSeedStatus;
bool skipChecking;
bool hasRootFolder;
bool forced;
bool paused;
bool addForced;
bool addPaused;
int uploadLimit;
int downloadLimit;
// for new torrents
QVector<int> filePriorities;
// for restored torrents
// for resumed torrents
qreal ratioLimit;
int seedingTimeLimit;
CreateTorrentParams();
CreateTorrentParams(const AddTorrentParams &params);
AddTorrentData();
AddTorrentData(const AddTorrentParams &params);
};
struct TrackerInfo
@@ -149,8 +149,6 @@ namespace BitTorrent
PausedDownloading,
PausedUploading,
Moving,
MissingFiles,
Error
};
@@ -158,7 +156,6 @@ namespace BitTorrent
class TorrentHandle : public QObject
{
Q_DISABLE_COPY(TorrentHandle)
Q_DECLARE_TR_FUNCTIONS(BitTorrent::TorrentHandle)
public:
static const qreal USE_GLOBAL_RATIO;
@@ -171,7 +168,7 @@ namespace BitTorrent
static const int MAX_SEEDING_TIME;
TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
const CreateTorrentParams &params);
const AddTorrentData &data);
~TorrentHandle();
bool isValid() const;
@@ -334,7 +331,7 @@ namespace BitTorrent
void setName(const QString &name);
void setSequentialDownload(bool b);
void toggleSequentialDownload();
void setFirstLastPiecePriority(bool enabled);
void setFirstLastPiecePriority(bool b);
void toggleFirstLastPiecePriority();
void pause();
void resume(bool forced = false);
@@ -348,6 +345,7 @@ 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);
@@ -355,7 +353,7 @@ namespace BitTorrent
void setSuperSeeding(bool enable);
void flushCache();
void addTrackers(const QList<TrackerEntry> &trackers);
void replaceTrackers(const QList<TrackerEntry> &trackers);
void replaceTrackers(QList<TrackerEntry> trackers);
void addUrlSeeds(const QList<QUrl> &urlSeeds);
void removeUrlSeeds(const QList<QUrl> &urlSeeds);
bool connectPeer(const PeerAddress &peerAddress);
@@ -372,7 +370,7 @@ namespace BitTorrent
void handleTempPathChanged();
void handleCategorySavePathChanged();
void handleAppendExtensionToggled();
void saveResumeData();
void saveResumeData(bool updateStatus = false);
/**
* @brief fraction of file pieces that are available at least from one peer
@@ -390,25 +388,24 @@ namespace BitTorrent
void updateState();
void updateTorrentInfo();
void handleStorageMovedAlert(const libtorrent::storage_moved_alert *p);
void handleStorageMovedFailedAlert(const libtorrent::storage_moved_failed_alert *p);
void handleTrackerReplyAlert(const libtorrent::tracker_reply_alert *p);
void handleTrackerWarningAlert(const libtorrent::tracker_warning_alert *p);
void handleTrackerErrorAlert(const libtorrent::tracker_error_alert *p);
void handleTorrentCheckedAlert(const libtorrent::torrent_checked_alert *p);
void handleTorrentFinishedAlert(const libtorrent::torrent_finished_alert *p);
void handleTorrentPausedAlert(const libtorrent::torrent_paused_alert *p);
void handleTorrentResumedAlert(const libtorrent::torrent_resumed_alert *p);
void handleSaveResumeDataAlert(const libtorrent::save_resume_data_alert *p);
void handleSaveResumeDataFailedAlert(const libtorrent::save_resume_data_failed_alert *p);
void handleFastResumeRejectedAlert(const libtorrent::fastresume_rejected_alert *p);
void handleFileRenamedAlert(const libtorrent::file_renamed_alert *p);
void handleFileRenameFailedAlert(const libtorrent::file_rename_failed_alert *p);
void handleFileCompletedAlert(const libtorrent::file_completed_alert *p);
void handleMetadataReceivedAlert(const libtorrent::metadata_received_alert *p);
void handleStatsAlert(const libtorrent::stats_alert *p);
void handleStorageMovedAlert(libtorrent::storage_moved_alert *p);
void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p);
void handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p);
void handleTrackerWarningAlert(libtorrent::tracker_warning_alert *p);
void handleTrackerErrorAlert(libtorrent::tracker_error_alert *p);
void handleTorrentCheckedAlert(libtorrent::torrent_checked_alert *p);
void handleTorrentFinishedAlert(libtorrent::torrent_finished_alert *p);
void handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p);
void handleTorrentResumedAlert(libtorrent::torrent_resumed_alert *p);
void handleSaveResumeDataAlert(libtorrent::save_resume_data_alert *p);
void handleSaveResumeDataFailedAlert(libtorrent::save_resume_data_failed_alert *p);
void handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert *p);
void handleFileRenamedAlert(libtorrent::file_renamed_alert *p);
void handleFileRenameFailedAlert(libtorrent::file_rename_failed_alert *p);
void handleFileCompletedAlert(libtorrent::file_completed_alert *p);
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p);
void handleStatsAlert(libtorrent::stats_alert *p);
void resume_impl(bool forced);
bool isMoveInProgress() const;
QString nativeActualSavePath() const;
@@ -420,7 +417,6 @@ namespace BitTorrent
bool addTracker(const TrackerEntry &tracker);
bool addUrlSeed(const QUrl &urlSeed);
bool removeUrlSeed(const QUrl &urlSeed);
void setFirstLastPiecePriorityImpl(bool enabled, const QVector<int> &updatedFilePrio = {});
Session *const m_session;
libtorrent::torrent_handle m_nativeHandle;
@@ -460,19 +456,10 @@ namespace BitTorrent
bool m_hasMissingFiles;
bool m_hasRootFolder;
bool m_needsToSetFirstLastPiecePriority;
bool m_needsToStartForced;
bool m_pauseAfterRecheck;
bool m_needSaveResumeData;
QHash<QString, TrackerInfo> m_trackerInfos;
enum StartupState
{
NotStarted,
Starting,
Started
};
StartupState m_startupState = NotStarted;
bool m_unchecked = false;
};
}

View File

@@ -26,21 +26,20 @@
* exception statement from your version.
*/
#include "torrentinfo.h"
#include <QDebug>
#include <QString>
#include <QList>
#include <QUrl>
#include <QDateTime>
#include <libtorrent/error_code.hpp>
#include <QDateTime>
#include <QDebug>
#include <QString>
#include <QUrl>
#include "base/global.h"
#include "base/utils/fs.h"
#include "base/utils/misc.h"
#include "base/utils/fs.h"
#include "base/utils/string.h"
#include "infohash.h"
#include "trackerentry.h"
#include "torrentinfo.h"
namespace libt = libtorrent;
using namespace BitTorrent;
@@ -61,75 +60,23 @@ TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other)
return *this;
}
TorrentInfo TorrentInfo::load(const QByteArray &data, QString *error) noexcept
TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString &error)
{
// 2-step construction to overcome default limits of `depth_limit` & `token_limit` which are
// used in `torrent_info()` constructor
const int depthLimit = 100;
const int tokenLimit = 10000000;
error.clear();
libt::error_code ec;
#if LIBTORRENT_VERSION_NUM < 10100
libt::lazy_entry node;
libt::lazy_bdecode(data.constData(), (data.constData() + data.size()), node, ec
, nullptr, depthLimit, tokenLimit);
#else
libt::bdecode_node node;
bdecode(data.constData(), (data.constData() + data.size()), node, ec
, nullptr, depthLimit, tokenLimit);
#endif
TorrentInfo info(NativePtr(new libt::torrent_info(Utils::Fs::toNativePath(path).toStdString(), ec)));
if (ec) {
if (error)
*error = QString::fromStdString(ec.message());
return TorrentInfo();
}
TorrentInfo info {NativePtr(new libt::torrent_info(node, ec))};
if (ec) {
if (error)
*error = QString::fromStdString(ec.message());
return TorrentInfo();
error = QString::fromUtf8(ec.message().c_str());
qDebug("Cannot load .torrent file: %s", qUtf8Printable(error));
}
return info;
}
TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexcept
TorrentInfo TorrentInfo::loadFromFile(const QString &path)
{
if (error)
error->clear();
QFile file {path};
if (!file.open(QIODevice::ReadOnly)) {
if (error)
*error = file.errorString();
return TorrentInfo();
}
if (file.size() > MAX_TORRENT_SIZE) {
if (error)
*error = tr("File size exceeds max limit %1").arg(Utils::Misc::friendlyUnit(MAX_TORRENT_SIZE));
return TorrentInfo();
}
QByteArray data;
try {
data = file.readAll();
}
catch (const std::bad_alloc &e) {
if (error)
*error = tr("Torrent file read error: %1").arg(e.what());
return TorrentInfo();
}
if (data.size() != file.size()) {
if (error)
*error = tr("Torrent file read error: size mismatch");
return TorrentInfo();
}
file.close();
return load(data, error);
QString error;
return loadFromFile(path, error);
}
bool TorrentInfo::isValid() const
@@ -247,7 +194,7 @@ QList<TrackerEntry> TorrentInfo::trackers() const
if (!isValid()) return QList<TrackerEntry>();
QList<TrackerEntry> trackers;
for (const libt::announce_entry &tracker : m_nativeInfo->trackers())
foreach (const libt::announce_entry &tracker, m_nativeInfo->trackers())
trackers.append(tracker);
return trackers;
@@ -258,7 +205,7 @@ QList<QUrl> TorrentInfo::urlSeeds() const
if (!isValid()) return QList<QUrl>();
QList<QUrl> urlSeeds;
for (const libt::web_seed_entry &webSeed : m_nativeInfo->web_seeds())
foreach (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()));
@@ -314,7 +261,7 @@ QVector<QByteArray> TorrentInfo::pieceHashes() const
return hashes;
}
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString &file) const
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString& file) const
{
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
return {};
@@ -339,21 +286,21 @@ 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 auto firstOffset = files.file_offset(fileIndex);
return makeInterval(static_cast<int>(firstOffset / pieceLength()),
static_cast<int>((firstOffset + fileSize - 1) / pieceLength()));
}
void TorrentInfo::renameFile(const int index, const QString &newPath)
void TorrentInfo::renameFile(uint index, const QString &newPath)
{
if (!isValid()) return;
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
// the check whether the object valid is not needed here
// because filesCount() returns -1 in that case and the loop exits immediately
for (int i = 0; i < filesCount(); ++i)
if (fileName == filePath(i))
return i;
@@ -361,29 +308,24 @@ int BitTorrent::TorrentInfo::fileIndex(const QString &fileName) const
return -1;
}
QString TorrentInfo::rootFolder() const
bool TorrentInfo::hasRootFolder() const
{
QString rootFolder;
QString testRootFolder;
for (int i = 0; i < filesCount(); ++i) {
const QString filePath = this->filePath(i);
if (QDir::isAbsolutePath(filePath)) continue;
const auto filePathElements = filePath.splitRef('/');
// if at least one file has no root folder, no common root folder exists
if (filePathElements.count() <= 1) return "";
if (filePathElements.count() <= 1) return false;
if (rootFolder.isEmpty())
rootFolder = filePathElements.at(0).toString();
else if (rootFolder != filePathElements.at(0))
return "";
if (testRootFolder.isEmpty())
testRootFolder = filePathElements.at(0).toString();
else if (testRootFolder != filePathElements.at(0))
return false;
}
return rootFolder;
}
bool TorrentInfo::hasRootFolder() const
{
return !rootFolder().isEmpty();
return true;
}
void TorrentInfo::stripRootFolder()

View File

@@ -29,21 +29,20 @@
#ifndef BITTORRENT_TORRENTINFO_H
#define BITTORRENT_TORRENTINFO_H
#include <QtGlobal>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/version.hpp>
#include <QCoreApplication>
#include <QList>
#include <QtGlobal>
#include <QVector>
#include "base/indexrange.h"
class QByteArray;
class QDateTime;
class QString;
class QStringList;
class QUrl;
class QDateTime;
class QStringList;
class QByteArray;
template<typename T> class QList;
template<typename T> class QVector;
namespace BitTorrent
{
@@ -52,8 +51,6 @@ namespace BitTorrent
class TorrentInfo
{
Q_DECLARE_TR_FUNCTIONS(TorrentInfo)
public:
#if LIBTORRENT_VERSION_NUM < 10100
typedef boost::intrusive_ptr<const libtorrent::torrent_info> NativeConstPtr;
@@ -66,8 +63,8 @@ namespace BitTorrent
explicit TorrentInfo(NativeConstPtr nativeInfo = NativeConstPtr());
TorrentInfo(const TorrentInfo &other);
static TorrentInfo load(const QByteArray &data, QString *error = nullptr) noexcept;
static TorrentInfo loadFromFile(const QString &path, QString *error = nullptr) noexcept;
static TorrentInfo loadFromFile(const QString &path, QString &error);
static TorrentInfo loadFromFile(const QString &path);
TorrentInfo &operator=(const TorrentInfo &other);
@@ -102,9 +99,8 @@ namespace BitTorrent
PieceRange filePieces(const QString &file) const;
PieceRange filePieces(int fileIndex) const;
void renameFile(int index, const QString &newPath);
void renameFile(uint index, const QString &newPath);
QString rootFolder() const;
bool hasRootFolder() const;
void stripRootFolder();

View File

@@ -1,5 +1,5 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Bittorrent Client using Qt4 and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
@@ -27,18 +27,15 @@
* exception statement from your version.
*/
#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"
#include "tracker.h"
// static limits
static const int MAX_TORRENTS = 100;
@@ -60,7 +57,7 @@ bool Peer::operator==(const Peer &other) const
QString Peer::uid() const
{
return ip.toString() + ':' + QString::number(port);
return ip + ":" + QString::number(port);
}
libtorrent::entry Peer::toEntry(bool noPeerId) const
@@ -68,7 +65,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.toString().toStdString());
peerMap["ip"] = libtorrent::entry(ip.toStdString());
peerMap["port"] = libtorrent::entry(port);
return libtorrent::entry(peerMap);
@@ -77,7 +74,7 @@ libtorrent::entry Peer::toEntry(bool noPeerId) const
// Tracker
Tracker::Tracker(QObject *parent)
: QObject(parent)
: Http::ResponseBuilder(parent)
, m_server(new Http::Server(this, this))
{
}
@@ -133,35 +130,19 @@ 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 : asConst(splitToViews(m_request.query, "&"))) {
const int sepPos = param.indexOf('=');
if (sepPos <= 0) continue; // ignores params without name
const QByteArray nameComponent = midView(param, 0, sepPos);
const QByteArray valueComponent = midView(param, (sepPos + 1));
const QString paramName = QString::fromUtf8(QByteArray::fromPercentEncoding(nameComponent));
const QByteArray paramValue = QByteArray::fromPercentEncoding(valueComponent);
queryParams[paramName] = paramValue;
}
TrackerAnnounceRequest announceReq;
const QStringMap &gets = m_request.gets;
TrackerAnnounceRequest annonceReq;
// IP
// 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;
annonceReq.peer.ip = m_env.clientAddress.toString();
// 1. Get info_hash
if (!queryParams.contains("info_hash")) {
if (!gets.contains("info_hash")) {
qDebug("Tracker: Missing info_hash");
status(101, "Missing info_hash");
return;
}
announceReq.infoHash = queryParams.value("info_hash");
annonceReq.infoHash = gets.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,12 +151,12 @@ void Tracker::respondToAnnounceRequest()
}*/
// 2. Get peer ID
if (!queryParams.contains("peer_id")) {
if (!gets.contains("peer_id")) {
qDebug("Tracker: Missing peer_id");
status(102, "Missing peer_id");
return;
}
announceReq.peer.peerId = queryParams.value("peer_id");
annonceReq.peer.peerId = gets.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));
@@ -184,102 +165,92 @@ void Tracker::respondToAnnounceRequest()
}*/
// 3. Get port
if (!queryParams.contains("port")) {
if (!gets.contains("port")) {
qDebug("Tracker: Missing port");
status(103, "Missing port");
return;
}
bool ok = false;
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);
annonceReq.peer.port = gets.value("port").toInt(&ok);
if (!ok || annonceReq.peer.port < 1 || annonceReq.peer.port > 65535) {
qDebug("Tracker: Invalid port number (%d)", annonceReq.peer.port);
status(103, "Missing port");
return;
}
// 4. Get event
announceReq.event = "";
if (queryParams.contains("event")) {
announceReq.event = queryParams.value("event");
qDebug("Tracker: event is %s", qUtf8Printable(announceReq.event));
annonceReq.event = "";
if (gets.contains("event")) {
annonceReq.event = gets.value("event");
qDebug("Tracker: event is %s", qUtf8Printable(annonceReq.event));
}
// 5. Get numwant
announceReq.numwant = 50;
if (queryParams.contains("numwant")) {
int tmp = queryParams.value("numwant").toInt();
annonceReq.numwant = 50;
if (gets.contains("numwant")) {
int tmp = gets.value("numwant").toInt();
if (tmp > 0) {
qDebug("Tracker: numwant = %d", tmp);
announceReq.numwant = tmp;
annonceReq.numwant = tmp;
}
}
// 6. no_peer_id (extension)
announceReq.noPeerId = false;
if (queryParams.contains("no_peer_id"))
announceReq.noPeerId = true;
annonceReq.noPeerId = false;
if (gets.contains("no_peer_id"))
annonceReq.noPeerId = true;
// 7. TODO: support "compact" extension
// Done parsing, now let's reply
if (announceReq.event == "stopped") {
unregisterPeer(announceReq);
if (m_torrents.contains(annonceReq.infoHash)) {
if (annonceReq.event == "stopped") {
qDebug("Tracker: Peer stopped downloading, deleting it from the list");
m_torrents[annonceReq.infoHash].remove(annonceReq.peer.uid());
return;
}
}
else {
registerPeer(announceReq);
replyWithPeerList(announceReq);
}
}
void Tracker::registerPeer(const TrackerAnnounceRequest &announceReq)
{
if (announceReq.peer.port == 0) return;
if (!m_torrents.contains(announceReq.infoHash)) {
// Unknown torrent
if (m_torrents.size() == MAX_TORRENTS) {
// Reached max size, remove a random torrent
m_torrents.erase(m_torrents.begin());
}
}
// Register the user
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());
}
PeerList peers = m_torrents.value(annonceReq.infoHash);
if (peers.size() == MAX_PEERS_PER_TORRENT) {
// Too many peers, remove a random one
peers.erase(peers.begin());
}
peers[announceReq.peer.uid()] = announceReq.peer;
peers[annonceReq.peer.uid()] = annonceReq.peer;
m_torrents[annonceReq.infoHash] = peers;
// Reply
replyWithPeerList(annonceReq);
}
void Tracker::unregisterPeer(const TrackerAnnounceRequest &announceReq)
{
if (announceReq.peer.port == 0) return;
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 &announceReq)
void Tracker::replyWithPeerList(const TrackerAnnounceRequest &annonceReq)
{
// Prepare the entry for bencoding
libtorrent::entry::dictionary_type replyDict;
replyDict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL);
QList<Peer> peers = m_torrents.value(annonceReq.infoHash).values();
libtorrent::entry::list_type peerList;
for (const Peer &p : m_torrents.value(announceReq.infoHash))
peerList.push_back(p.toEntry(announceReq.noPeerId));
foreach (const Peer &p, peers) {
//if (p != annonce_req.peer)
peerList.push_back(p.toEntry(annonceReq.noPeerId));
}
replyDict["peers"] = libtorrent::entry(peerList);
const libtorrent::entry replyEntry(replyDict);
libtorrent::entry replyEntry(replyDict);
// bencode
QByteArray reply;
libtorrent::bencode(std::back_inserter(reply), replyEntry);
std::vector<char> buf;
libtorrent::bencode(std::back_inserter(buf), replyEntry);
QByteArray reply(&buf[0], static_cast<int>(buf.size()));
qDebug("Tracker: reply with the following bencoded data:\n %s", reply.constData());
// HTTP reply
print(reply, Http::CONTENT_TYPE_TXT);
}

View File

@@ -31,8 +31,6 @@
#define BITTORRENT_TRACKER_H
#include <QHash>
#include <QObject>
#include <QHostAddress>
#include "base/http/irequesthandler.h"
#include "base/http/responsebuilder.h"
@@ -52,8 +50,8 @@ namespace BitTorrent
{
struct Peer
{
QHostAddress ip;
QByteArray peerId;
QString ip;
QString peerId;
int port;
bool operator!=(const Peer &other) const;
@@ -64,7 +62,7 @@ namespace BitTorrent
struct TrackerAnnounceRequest
{
QByteArray infoHash;
QString infoHash;
QString event;
int numwant;
Peer peer;
@@ -73,11 +71,11 @@ namespace BitTorrent
};
typedef QHash<QString, Peer> PeerList;
typedef QHash<QByteArray, PeerList> TorrentList;
typedef QHash<QString, PeerList> TorrentList;
/* Basic Bittorrent tracker implementation in Qt */
/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */
class Tracker : public QObject, public Http::IRequestHandler, private Http::ResponseBuilder
class Tracker : public Http::ResponseBuilder, public Http::IRequestHandler
{
Q_OBJECT
Q_DISABLE_COPY(Tracker)
@@ -91,9 +89,7 @@ namespace BitTorrent
private:
void respondToAnnounceRequest();
void registerPeer(const TrackerAnnounceRequest &announceReq);
void unregisterPeer(const TrackerAnnounceRequest &announceReq);
void replyWithPeerList(const TrackerAnnounceRequest &announceReq);
void replyWithPeerList(const TrackerAnnounceRequest &annonceReq);
Http::Server *m_server;
TorrentList m_torrents;

View File

@@ -26,12 +26,11 @@
* exception statement from your version.
*/
#include "trackerentry.h"
#include <QString>
#include "base/utils/misc.h"
#include "base/utils/string.h"
#include "trackerentry.h"
using namespace BitTorrent;

View File

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

View File

@@ -1,39 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* 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 "exceptions.h"
RuntimeError::RuntimeError(const QString &message)
: std::runtime_error {message.toUtf8().data()}
{
}
QString RuntimeError::message() const
{
return what();
}

View File

@@ -1,120 +1,143 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "filesystemwatcher.h"
#include <QtGlobal>
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
#include <cstring>
#include <sys/mount.h>
#ifndef Q_OS_WIN
#include <QSet>
#include <iostream>
#include <errno.h>
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
#include <sys/param.h>
#include <sys/mount.h>
#include <string.h>
#elif !defined Q_OS_HAIKU
#include <sys/vfs.h>
#endif
#endif
#include "base/algorithm.h"
#include "base/bittorrent/magneturi.h"
#include "base/bittorrent/torrentinfo.h"
#include "base/global.h"
#include "base/logger.h"
#include "base/preferences.h"
#include "base/utils/fs.h"
#include "filesystemwatcher.h"
namespace
{
const int WATCH_INTERVAL = 10000; // 10 sec
const int MAX_PARTIAL_RETRIES = 5;
}
#ifndef CIFS_MAGIC_NUMBER
#define CIFS_MAGIC_NUMBER 0xFF534D42
#endif
#ifndef NFS_SUPER_MAGIC
#define NFS_SUPER_MAGIC 0x6969
#endif
#ifndef SMB_SUPER_MAGIC
#define SMB_SUPER_MAGIC 0x517B
#endif
const int WATCH_INTERVAL = 10000; // 10 sec
const int MAX_PARTIAL_RETRIES = 5;
FileSystemWatcher::FileSystemWatcher(QObject *parent)
: QFileSystemWatcher(parent)
{
connect(this, &QFileSystemWatcher::directoryChanged, this, &FileSystemWatcher::scanLocalFolder);
m_filters << "*.torrent" << "*.magnet";
connect(this, SIGNAL(directoryChanged(QString)), SLOT(scanLocalFolder(QString)));
}
m_partialTorrentTimer.setSingleShot(true);
connect(&m_partialTorrentTimer, &QTimer::timeout, this, &FileSystemWatcher::processPartialTorrents);
connect(&m_watchTimer, &QTimer::timeout, this, &FileSystemWatcher::scanNetworkFolders);
FileSystemWatcher::~FileSystemWatcher()
{
#ifndef Q_OS_WIN
if (m_watchTimer)
delete m_watchTimer;
#endif
if (m_partialTorrentTimer)
delete m_partialTorrentTimer;
}
QStringList FileSystemWatcher::directories() const
{
QStringList dirs = QFileSystemWatcher::directories();
for (const QDir &dir : asConst(m_watchedFolders))
dirs << dir.canonicalPath();
QStringList dirs;
#ifndef Q_OS_WIN
if (m_watchTimer) {
foreach (const QDir &dir, m_watchedFolders)
dirs << dir.canonicalPath();
}
#endif
dirs << QFileSystemWatcher::directories();
return dirs;
}
void FileSystemWatcher::addPath(const QString &path)
{
if (path.isEmpty()) return;
#if !defined Q_OS_HAIKU
const QDir dir(path);
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
QDir dir(path);
if (!dir.exists()) return;
// Check if the path points to a network file system or not
if (Utils::Fs::isNetworkFileSystem(path)) {
if (isNetworkFileSystem(path)) {
// Network mode
LogMsg(tr("Watching remote folder: \"%1\"").arg(Utils::Fs::toNativePath(path)));
qDebug("Network folder detected: %s", qUtf8Printable(path));
qDebug("Using file polling mode instead of inotify...");
m_watchedFolders << dir;
m_watchTimer.start(WATCH_INTERVAL);
return;
// Set up the watch timer
if (!m_watchTimer) {
m_watchTimer = new QTimer(this);
connect(m_watchTimer, SIGNAL(timeout()), SLOT(scanNetworkFolders()));
m_watchTimer->start(WATCH_INTERVAL); // 5 sec
}
}
else {
#endif
// Normal mode
qDebug("FS Watching is watching %s in normal mode", qUtf8Printable(path));
QFileSystemWatcher::addPath(path);
scanLocalFolder(path);
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
}
#endif
// Normal mode
LogMsg(tr("Watching local folder: \"%1\"").arg(Utils::Fs::toNativePath(path)));
QFileSystemWatcher::addPath(path);
scanLocalFolder(path);
}
void FileSystemWatcher::removePath(const QString &path)
{
if (m_watchedFolders.removeOne(path)) {
if (m_watchedFolders.isEmpty())
m_watchTimer.stop();
return;
#ifndef Q_OS_WIN
QDir dir(path);
for (int i = 0; i < m_watchedFolders.count(); ++i) {
if (QDir(m_watchedFolders.at(i)) == dir) {
m_watchedFolders.removeAt(i);
if (m_watchedFolders.isEmpty())
delete m_watchTimer;
return;
}
}
#endif
// Normal mode
QFileSystemWatcher::removePath(path);
}
void FileSystemWatcher::scanLocalFolder(const QString &path)
void FileSystemWatcher::scanLocalFolder(QString path)
{
QTimer::singleShot(2000, this, [this, path]() { processTorrentsInDir(path); });
qDebug("scanLocalFolder(%s) called", qUtf8Printable(path));
QStringList torrents;
// Local folders scan
addTorrentsFromDir(QDir(path), torrents);
// Report detected torrent files
if (!torrents.empty()) {
qDebug("The following files are being reported: %s", qUtf8Printable(torrents.join("\n")));
emit torrentsAdded(torrents);
}
}
void FileSystemWatcher::scanNetworkFolders()
{
for (const QDir &dir : asConst(m_watchedFolders))
processTorrentsInDir(dir);
#ifndef Q_OS_WIN
qDebug("scanNetworkFolders() called");
QStringList torrents;
// Network folders scan
foreach (const QDir &dir, m_watchedFolders) {
//qDebug("FSWatcher: Polling manually folder %s", qUtf8Printable(dir.path()));
addTorrentsFromDir(dir, torrents);
}
// Report detected torrent files
if (!torrents.empty()) {
qDebug("The following files are being reported: %s", qUtf8Printable(torrents.join("\n")));
emit torrentsAdded(torrents);
}
#endif
}
void FileSystemWatcher::processPartialTorrents()
@@ -122,33 +145,32 @@ void FileSystemWatcher::processPartialTorrents()
QStringList noLongerPartial;
// Check which torrents are still partial
Dict::removeIf(m_partialTorrents, [&noLongerPartial](const QString &torrentPath, int &value)
{
if (!QFile::exists(torrentPath))
return true;
if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid()) {
foreach (const QString &torrentPath, m_partialTorrents.keys()) {
if (!QFile::exists(torrentPath)) {
m_partialTorrents.remove(torrentPath);
}
else if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid()) {
noLongerPartial << torrentPath;
return true;
m_partialTorrents.remove(torrentPath);
}
if (value >= MAX_PARTIAL_RETRIES) {
QFile::rename(torrentPath, torrentPath + ".qbt_rejected");
return true;
else if (m_partialTorrents[torrentPath] >= MAX_PARTIAL_RETRIES) {
m_partialTorrents.remove(torrentPath);
QFile::rename(torrentPath, torrentPath + ".invalid");
}
++value;
return false;
});
else {
++m_partialTorrents[torrentPath];
}
}
// Stop the partial timer if necessary
if (m_partialTorrents.empty()) {
m_partialTorrentTimer.stop();
m_partialTorrentTimer->stop();
m_partialTorrentTimer->deleteLater();
qDebug("No longer any partial torrent.");
}
else {
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
m_partialTorrentTimer.start(WATCH_INTERVAL);
m_partialTorrentTimer->start(WATCH_INTERVAL);
}
// Notify of new torrents
@@ -156,23 +178,99 @@ void FileSystemWatcher::processPartialTorrents()
emit torrentsAdded(noLongerPartial);
}
void FileSystemWatcher::processTorrentsInDir(const QDir &dir)
void FileSystemWatcher::startPartialTorrentTimer()
{
QStringList torrents;
const QStringList files = dir.entryList({"*.torrent", "*.magnet"}, QDir::Files);
for (const QString &file : files) {
Q_ASSERT(!m_partialTorrents.isEmpty());
if (!m_partialTorrentTimer) {
m_partialTorrentTimer = new QTimer();
connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents()));
m_partialTorrentTimer->setSingleShot(true);
m_partialTorrentTimer->start(WATCH_INTERVAL);
}
}
void FileSystemWatcher::addTorrentsFromDir(const QDir &dir, QStringList &torrents)
{
const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted);
foreach (const QString &file, files) {
const QString fileAbsPath = dir.absoluteFilePath(file);
if (file.endsWith(".magnet"))
if (fileAbsPath.endsWith(".magnet")) {
torrents << fileAbsPath;
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid())
}
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid()) {
torrents << fileAbsPath;
else if (!m_partialTorrents.contains(fileAbsPath))
m_partialTorrents[fileAbsPath] = 0;
}
else if (!m_partialTorrents.contains(fileAbsPath)) {
qDebug("Partial torrent detected at: %s", qUtf8Printable(fileAbsPath));
qDebug("Delay the file's processing...");
m_partialTorrents.insert(fileAbsPath, 0);
}
}
if (!torrents.empty())
emit torrentsAdded(torrents);
if (!m_partialTorrents.empty() && !m_partialTorrentTimer.isActive())
m_partialTorrentTimer.start(WATCH_INTERVAL);
if (!m_partialTorrents.empty())
startPartialTorrentTimer();
}
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
bool FileSystemWatcher::isNetworkFileSystem(QString path)
{
QString file = path;
if (!file.endsWith("/"))
file += "/";
file += ".";
struct statfs buf;
if (!statfs(file.toLocal8Bit().constData(), &buf)) {
#ifdef Q_OS_MAC
// XXX: should we make sure HAVE_STRUCT_FSSTAT_F_FSTYPENAME is defined?
return ((strcmp(buf.f_fstypename, "nfs") == 0) || (strcmp(buf.f_fstypename, "cifs") == 0) || (strcmp(buf.f_fstypename, "smbfs") == 0));
#else
return ((buf.f_type == static_cast<long>(CIFS_MAGIC_NUMBER))
|| (buf.f_type == static_cast<long>(NFS_SUPER_MAGIC))
|| (buf.f_type == static_cast<long>(SMB_SUPER_MAGIC)));
#endif
}
else {
std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl;
switch(errno) {
case EACCES:
std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl;
break;
case EFAULT:
std::cerr << "Buf or path points to an invalid address" << std::endl;
break;
case EINTR:
std::cerr << "This call was interrupted by a signal" << std::endl;
break;
case EIO:
std::cerr << "I/O Error" << std::endl;
break;
case ELOOP:
std::cerr << "Too many symlinks" << std::endl;
break;
case ENAMETOOLONG:
std::cerr << "path is too long" << std::endl;
break;
case ENOENT:
std::cerr << "The file referred by path does not exist" << std::endl;
break;
case ENOMEM:
std::cerr << "Insufficient kernel memory" << std::endl;
break;
case ENOSYS:
std::cerr << "The file system does not detect this call" << std::endl;
break;
case ENOTDIR:
std::cerr << "A component of the path is not a directory" << std::endl;
break;
case EOVERFLOW:
std::cerr << "Some values were too large to be represented in the struct" << std::endl;
break;
default:
std::cerr << "Unknown error" << std::endl;
}
std::cerr << "Errno: " << errno << std::endl;
return false;
}
}
#endif

View File

@@ -1,37 +1,10 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#ifndef FILESYSTEMWATCHER_H
#define FILESYSTEMWATCHER_H
#include <QDir>
#include <QFileSystemWatcher>
#include <QHash>
#include <QPointer>
#include <QStringList>
#include <QTimer>
@@ -45,6 +18,7 @@ class FileSystemWatcher : public QFileSystemWatcher
public:
explicit FileSystemWatcher(QObject *parent = nullptr);
~FileSystemWatcher();
QStringList directories() const;
void addPath(const QString &path);
@@ -54,19 +28,25 @@ signals:
void torrentsAdded(const QStringList &pathList);
protected slots:
void scanLocalFolder(const QString &path);
void processPartialTorrents();
void scanLocalFolder(QString path);
void scanNetworkFolders();
void processPartialTorrents();
private:
void processTorrentsInDir(const QDir &dir);
void startPartialTorrentTimer();
void addTorrentsFromDir(const QDir &dir, QStringList &torrents);
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
static bool isNetworkFileSystem(QString path);
#endif
#ifndef Q_OS_WIN
QList<QDir> m_watchedFolders;
QPointer<QTimer> m_watchTimer;
#endif
QStringList m_filters;
// Partial torrents
QHash<QString, int> m_partialTorrents;
QTimer m_partialTorrentTimer;
QList<QDir> m_watchedFolders;
QTimer m_watchTimer;
QPointer<QTimer> m_partialTorrentTimer;
};
#endif // FILESYSTEMWATCHER_H

View File

@@ -26,21 +26,5 @@
* exception statement from your version.
*/
#pragma once
#include <type_traits>
#include <QtGlobal>
const char C_TORRENT_FILE_EXTENSION[] = ".torrent";
const int MAX_TORRENT_SIZE = 100 * 1024 * 1024; // 100 MiB
template <typename T>
constexpr typename std::add_const<T>::type &asConst(T &t) noexcept { return t; }
// Forward rvalue as const
template <typename T>
constexpr typename std::add_const<T>::type asConst(T &&t) noexcept { return std::move(t); }
// Prevent const rvalue arguments
template <typename T>
void asConst(const T &&) = delete;

View File

@@ -1,8 +1,7 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Mike Tzou (Chocobo1)
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -26,13 +25,15 @@
* 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.
*
* Contact : chris@qbittorrent.org
*/
#include "connection.h"
#include <QRegExp>
#include <QTcpSocket>
#include "base/logger.h"
#include "irequesthandler.h"
#include "requestparser.h"
#include "responsegenerator.h"
@@ -46,7 +47,7 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj
{
m_socket->setParent(this);
m_idleTimer.start();
connect(m_socket, &QTcpSocket::readyRead, this, &Connection::read);
connect(m_socket, SIGNAL(readyRead()), SLOT(read()));
}
Connection::~Connection()
@@ -57,64 +58,37 @@ Connection::~Connection()
void Connection::read()
{
m_idleTimer.restart();
m_receivedData.append(m_socket->readAll());
Request request;
RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request); // TODO: transform request headers to lowercase
while (!m_receivedData.isEmpty()) {
const RequestParser::ParseResult result = RequestParser::parse(m_receivedData);
switch (err) {
case RequestParser::IncompleteRequest:
// Partial request waiting for the rest
break;
switch (result.status) {
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: %1, IP: %2")
.arg(bufferLimit).arg(m_socket->peerAddress().toString()), Log::WARNING);
case RequestParser::BadRequest:
sendResponse(Response(400, "Bad Request"));
m_receivedData.clear();
break;
Response resp(413, "Payload Too Large");
resp.headers[HEADER_CONNECTION] = "close";
case RequestParser::NoError:
const Environment env {m_socket->localAddress(), m_socket->localPort(), m_socket->peerAddress(), m_socket->peerPort()};
sendResponse(resp);
m_socket->close();
}
}
return;
case RequestParser::ParseStatus::BadRequest: {
Logger::instance()->addMessage(tr("Bad Http request, closing socket. IP: %1")
.arg(m_socket->peerAddress().toString()), Log::WARNING);
Response resp(400, "Bad Request");
resp.headers[HEADER_CONNECTION] = "close";
sendResponse(resp);
m_socket->close();
}
return;
case RequestParser::ParseStatus::OK: {
const Environment env {m_socket->localAddress(), m_socket->localPort(), m_socket->peerAddress(), m_socket->peerPort()};
Response resp = m_requestHandler->processRequest(result.request, env);
if (acceptsGzipEncoding(result.request.headers["accept-encoding"]))
resp.headers[HEADER_CONTENT_ENCODING] = "gzip";
resp.headers[HEADER_CONNECTION] = "keep-alive";
sendResponse(resp);
m_receivedData = m_receivedData.mid(result.frameSize);
}
break;
default:
Q_ASSERT(false);
return;
}
Response response = m_requestHandler->processRequest(request, env);
if (acceptsGzipEncoding(request.headers["accept-encoding"]))
response.headers[HEADER_CONTENT_ENCODING] = "gzip";
sendResponse(response);
m_receivedData.clear();
break;
}
}
void Connection::sendResponse(const Response &response) const
void Connection::sendResponse(const Response &response)
{
m_socket->write(toByteArray(response));
m_socket->close(); // TODO: remove when HTTP pipelining is supported
}
bool Connection::hasExpired(const qint64 timeout) const
@@ -133,7 +107,7 @@ bool Connection::acceptsGzipEncoding(QString codings)
const auto isCodingAvailable = [](const QStringList &list, const QString &encoding) -> bool
{
for (const QString &str : list) {
foreach (const QString &str, list) {
if (!str.startsWith(encoding))
continue;

View File

@@ -1,7 +1,7 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -25,6 +25,8 @@
* 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.
*
* Contact : chris@qbittorrent.org
*/
@@ -48,7 +50,7 @@ namespace Http
Q_DISABLE_COPY(Connection)
public:
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = nullptr);
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
~Connection();
bool hasExpired(qint64 timeout) const;
@@ -59,7 +61,7 @@ namespace Http
private:
static bool acceptsGzipEncoding(QString codings);
void sendResponse(const Response &response) const;
void sendResponse(const Response &response);
QTcpSocket *m_socket;
IRequestHandler *m_requestHandler;

View File

@@ -1,81 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#include "httperror.h"
HTTPError::HTTPError(int statusCode, const QString &statusText, const QString &message)
: RuntimeError {message}
, m_statusCode {statusCode}
, m_statusText {statusText}
{
}
int HTTPError::statusCode() const
{
return m_statusCode;
}
QString HTTPError::statusText() const
{
return m_statusText;
}
BadRequestHTTPError::BadRequestHTTPError(const QString &message)
: HTTPError(400, QLatin1String("Bad Request"), message)
{
}
ConflictHTTPError::ConflictHTTPError(const QString &message)
: HTTPError(409, QLatin1String("Conflict"), message)
{
}
ForbiddenHTTPError::ForbiddenHTTPError(const QString &message)
: HTTPError(403, QLatin1String("Forbidden"), message)
{
}
NotFoundHTTPError::NotFoundHTTPError(const QString &message)
: HTTPError(404, QLatin1String("Not Found"), message)
{
}
UnsupportedMediaTypeHTTPError::UnsupportedMediaTypeHTTPError(const QString &message)
: HTTPError(415, QLatin1String("Unsupported Media Type"), message)
{
}
UnauthorizedHTTPError::UnauthorizedHTTPError(const QString &message)
: HTTPError(401, QLatin1String("Unauthorized"), message)
{
}
InternalServerErrorHTTPError::InternalServerErrorHTTPError(const QString &message)
: HTTPError(500, QLatin1String("Internal Server Error"), message)
{
}

View File

@@ -1,86 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2017 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
#include "base/exceptions.h"
class HTTPError : public RuntimeError
{
public:
HTTPError(int statusCode, const QString &statusText, const QString &message = "");
int statusCode() const;
QString statusText() const;
private:
const int m_statusCode;
const QString m_statusText;
};
class BadRequestHTTPError : public HTTPError
{
public:
explicit BadRequestHTTPError(const QString &message = "");
};
class ForbiddenHTTPError : public HTTPError
{
public:
explicit ForbiddenHTTPError(const QString &message = "");
};
class NotFoundHTTPError : public HTTPError
{
public:
explicit NotFoundHTTPError(const QString &message = "");
};
class ConflictHTTPError : public HTTPError
{
public:
explicit ConflictHTTPError(const QString &message = "");
};
class UnsupportedMediaTypeHTTPError : public HTTPError
{
public:
explicit UnsupportedMediaTypeHTTPError(const QString &message = "");
};
class UnauthorizedHTTPError : public HTTPError
{
public:
explicit UnauthorizedHTTPError(const QString &message = "");
};
class InternalServerErrorHTTPError : public HTTPError
{
public:
explicit InternalServerErrorHTTPError(const QString &message = "");
};

View File

@@ -1,6 +1,5 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Mike Tzou (Chocobo1)
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
*
@@ -31,221 +30,242 @@
#include "requestparser.h"
#include <QDebug>
#include <QRegularExpression>
#include <QDir>
#include <QStringList>
#include <QUrl>
#include <QUrlQuery>
#include "base/utils/bytearray.h"
#include "base/utils/string.h"
const QByteArray EOL("\r\n");
const QByteArray EOH("\r\n\r\n");
inline QString unquoted(const QString &str)
{
if ((str[0] == '\"') && (str[str.length() - 1] == '\"'))
return str.mid(1, str.length() - 2);
return str;
}
using namespace Http;
using namespace Utils::ByteArray;
using QStringPair = QPair<QString, QString>;
namespace
RequestParser::ErrorCode RequestParser::parse(const QByteArray &data, Request &request, uint maxContentLength)
{
const QByteArray EOH = QByteArray(CRLF).repeated(2);
const QByteArray viewWithoutEndingWith(const QByteArray &in, const QByteArray &str)
{
if (in.endsWith(str))
return QByteArray::fromRawData(in.constData(), (in.size() - str.size()));
return in;
}
bool parseHeaderLine(const QString &line, QStringMap &out)
{
// [rfc7230] 3.2. Header Fields
const int i = line.indexOf(':');
if (i <= 0) {
qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
return false;
}
const QString name = line.leftRef(i).trimmed().toString().toLower();
const QString value = line.midRef(i + 1).trimmed().toString();
out[name] = value;
return true;
}
return RequestParser(maxContentLength).parseHttpRequest(data, request);
}
RequestParser::RequestParser()
RequestParser::RequestParser(uint maxContentLength)
: m_maxContentLength(maxContentLength)
{
}
RequestParser::ParseResult RequestParser::parse(const QByteArray &data)
RequestParser::ErrorCode RequestParser::parseHttpRequest(const QByteArray &data, Request &request)
{
// Warning! Header names are converted to lowercase
return RequestParser().doParse(data);
}
m_request = Request();
RequestParser::ParseResult RequestParser::doParse(const QByteArray &data)
{
// we don't handle malformed requests which use double `LF` as delimiter
// Parse HTTP request header
const int headerEnd = data.indexOf(EOH);
if (headerEnd < 0) {
qDebug() << Q_FUNC_INFO << "incomplete request";
return {ParseStatus::Incomplete, Request(), 0};
return IncompleteRequest;
}
const QString httpHeaders = QString::fromLatin1(data.constData(), headerEnd);
if (!parseStartLines(httpHeaders)) {
if (!parseHttpHeader(data.left(headerEnd))) {
qWarning() << Q_FUNC_INFO << "header parsing error";
return {ParseStatus::BadRequest, Request(), 0};
return BadRequest;
}
const int headerLength = headerEnd + EOH.length();
// handle supported methods
if ((m_request.method == HEADER_REQUEST_METHOD_GET) || (m_request.method == HEADER_REQUEST_METHOD_HEAD))
return {ParseStatus::OK, m_request, headerLength};
if (m_request.method == HEADER_REQUEST_METHOD_POST) {
bool ok = false;
const int contentLength = m_request.headers[HEADER_CONTENT_LENGTH].toInt(&ok);
if (!ok || (contentLength < 0)) {
qWarning() << Q_FUNC_INFO << "bad request: content-length invalid";
return {ParseStatus::BadRequest, Request(), 0};
// Parse HTTP request message
if (m_request.headers.contains("content-length")) {
int contentLength = m_request.headers["content-length"].toInt();
if (contentLength < 0) {
qWarning() << Q_FUNC_INFO << "bad request: content-length is negative";
return BadRequest;
}
if (contentLength > MAX_CONTENT_SIZE) {
if (contentLength > static_cast<int>(m_maxContentLength)) {
qWarning() << Q_FUNC_INFO << "bad request: message too long";
return {ParseStatus::BadRequest, Request(), 0};
return BadRequest;
}
if (contentLength > 0) {
const QByteArray httpBodyView = midView(data, headerLength, contentLength);
if (httpBodyView.length() < contentLength) {
qDebug() << Q_FUNC_INFO << "incomplete request";
return {ParseStatus::Incomplete, Request(), 0};
}
if (!parsePostMessage(httpBodyView)) {
qWarning() << Q_FUNC_INFO << "message body parsing error";
return {ParseStatus::BadRequest, Request(), 0};
}
QByteArray content = data.mid(headerEnd + EOH.length(), contentLength);
if (content.length() < contentLength) {
qDebug() << Q_FUNC_INFO << "incomplete request";
return IncompleteRequest;
}
return {ParseStatus::OK, m_request, (headerLength + contentLength)};
if ((contentLength > 0) && !parseContent(content)) {
qWarning() << Q_FUNC_INFO << "message parsing error";
return BadRequest;
}
}
qWarning() << Q_FUNC_INFO << "unsupported request method: " << m_request.method;
return {ParseStatus::BadRequest, Request(), 0}; // TODO: SHOULD respond "501 Not Implemented"
// qDebug() << Q_FUNC_INFO;
// qDebug() << "HTTP Request header:";
// qDebug() << data.left(headerEnd) << "\n";
request = m_request;
return NoError;
}
bool RequestParser::parseStartLines(const QString &data)
bool RequestParser::parseStartingLine(const QString &line)
{
// we don't handle malformed request which uses `LF` for newline
const QVector<QStringRef> lines = data.splitRef(CRLF, QString::SkipEmptyParts);
const QRegExp rx("^([A-Z]+)\\s+(\\S+)\\s+HTTP/\\d\\.\\d$");
// [rfc7230] 3.2.2. Field Order
QStringList requestLines;
for (const auto &line : lines) {
if (line.at(0).isSpace() && !requestLines.isEmpty()) {
// continuation of previous line
requestLines.last() += line.toString();
}
else {
requestLines += line.toString();
if (rx.indexIn(line.trimmed()) >= 0) {
m_request.method = rx.cap(1);
QUrl url = QUrl::fromEncoded(rx.cap(2).toLatin1());
m_request.path = url.path(); // Path
// Parse GET parameters
QListIterator<QPair<QString, QString> > i(QUrlQuery(url).queryItems());
while (i.hasNext()) {
QPair<QString, QString> pair = i.next();
m_request.gets[pair.first] = pair.second;
}
return true;
}
if (requestLines.isEmpty())
return false;
if (!parseRequestLine(requestLines[0]))
return false;
for (auto i = ++(requestLines.begin()); i != requestLines.end(); ++i) {
if (!parseHeaderLine(*i, m_request.headers))
return false;
}
return true;
qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
return false;
}
bool RequestParser::parseRequestLine(const QString &line)
bool RequestParser::parseHeaderLine(const QString &line, QPair<QString, QString> &out)
{
// [rfc7230] 3.1.1. Request Line
const QRegularExpression re(QLatin1String("^([A-Z]+)\\s+(\\S+)\\s+HTTP\\/(\\d\\.\\d)$"));
const QRegularExpressionMatch match = re.match(line);
if (!match.hasMatch()) {
int i = line.indexOf(QLatin1Char(':'));
if (i == -1) {
qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
return false;
}
// Request Methods
m_request.method = match.captured(1);
out = qMakePair(line.left(i).trimmed().toLower(), line.mid(i + 1).trimmed());
return true;
}
// Request Target
// URL components should be separated before percent-decoding
// [rfc3986] 2.4 When to Encode or Decode
const QByteArray url {match.captured(2).toLatin1()};
const int sepPos = url.indexOf('?');
const QByteArray pathComponent = ((sepPos == -1) ? url : Utils::ByteArray::midView(url, 0, sepPos));
m_request.path = QString::fromUtf8(QByteArray::fromPercentEncoding(pathComponent));
if (sepPos >= 0)
m_request.query = url.mid(sepPos + 1);
bool RequestParser::parseHttpHeader(const QByteArray &data)
{
QString str = QString::fromUtf8(data);
QStringList lines = str.trimmed().split(EOL);
// HTTP-version
m_request.version = match.captured(3);
QStringList headerLines;
foreach (const QString &line, lines) {
if (line[0].isSpace()) { // header line continuation
if (!headerLines.isEmpty()) { // really continuation
headerLines.last() += QLatin1Char(' ');
headerLines.last() += line.trimmed();
}
}
else {
headerLines.append(line);
}
}
if (headerLines.isEmpty())
return false; // Empty header
QStringList::Iterator it = headerLines.begin();
if (!parseStartingLine(*it))
return false;
++it;
for (; it != headerLines.end(); ++it) {
QPair<QString, QString> header;
if (!parseHeaderLine(*it, header))
return false;
m_request.headers[header.first] = header.second;
}
return true;
}
bool RequestParser::parsePostMessage(const QByteArray &data)
QList<QByteArray> RequestParser::splitMultipartData(const QByteArray &data, const QByteArray &boundary)
{
// parse POST message-body
const QString contentType = m_request.headers[HEADER_CONTENT_TYPE];
const QString contentTypeLower = contentType.toLower();
QList<QByteArray> ret;
QByteArray sep = boundary + EOL;
const int sepLength = sep.size();
// application/x-www-form-urlencoded
if (contentTypeLower.startsWith(CONTENT_TYPE_FORM_ENCODED)) {
// [URL Standard] 5.1 application/x-www-form-urlencoded parsing
const QByteArray processedData = QByteArray(data).replace('+', ' ');
int start = 0, end = 0;
if ((end = data.indexOf(sep, start)) >= 0) {
start = end + sepLength; // skip first boundary
QListIterator<QStringPair> i(QUrlQuery(processedData).queryItems(QUrl::FullyDecoded));
while ((end = data.indexOf(sep, start)) >= 0) {
ret << data.mid(start, end - EOL.length() - start);
start = end + sepLength;
}
// last or single part
sep = boundary + "--" + EOL;
if ((end = data.indexOf(sep, start)) >= 0)
ret << data.mid(start, end - EOL.length() - start);
}
return ret;
}
bool RequestParser::parseContent(const QByteArray &data)
{
// Parse message content
qDebug() << Q_FUNC_INFO << "Content-Length: " << m_request.headers["content-length"];
qDebug() << Q_FUNC_INFO << "data.size(): " << data.size();
// Parse url-encoded POST data
if (m_request.headers["content-type"].startsWith("application/x-www-form-urlencoded")) {
QUrl url;
url.setQuery(data);
QListIterator<QPair<QString, QString> > i(QUrlQuery(url).queryItems(QUrl::FullyDecoded));
while (i.hasNext()) {
const QStringPair pair = i.next();
m_request.posts[pair.first] = pair.second;
QPair<QString, QString> pair = i.next();
m_request.posts[pair.first.toLower()] = pair.second;
}
return true;
}
// multipart/form-data
if (contentTypeLower.startsWith(CONTENT_TYPE_FORM_DATA)) {
// [rfc2046] 5.1.1. Common Syntax
// Parse multipart/form data (torrent file)
/**
data has the following format (if boundary is "cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5")
// find boundary delimiter
const QLatin1String boundaryFieldName("boundary=");
const int idx = contentType.indexOf(boundaryFieldName);
if (idx < 0) {
qWarning() << Q_FUNC_INFO << "Could not find boundary in multipart/form-data header!";
return false;
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
Content-Disposition: form-data; name=\"Filename\"
PB020344.torrent
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
Content-Disposition: form-data; name=\"torrentfile"; filename=\"PB020344.torrent\"
Content-Type: application/x-bittorrent
BINARY DATA IS HERE
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
Content-Disposition: form-data; name=\"Upload\"
Submit Query
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5--
**/
QString contentType = m_request.headers["content-type"];
if (contentType.startsWith("multipart/form-data")) {
const QRegExp boundaryRegexQuoted("boundary=\"([ \\w'()+,-\\./:=\\?]+)\"");
const QRegExp boundaryRegexNotQuoted("boundary=([\\w'()+,-\\./:=\\?]+)");
QByteArray boundary;
if (boundaryRegexQuoted.indexIn(contentType) < 0) {
if (boundaryRegexNotQuoted.indexIn(contentType) < 0) {
qWarning() << "Could not find boundary in multipart/form-data header!";
return false;
}
else {
boundary = "--" + boundaryRegexNotQuoted.cap(1).toLatin1();
}
}
else {
boundary = "--" + boundaryRegexQuoted.cap(1).toLatin1();
}
const QByteArray delimiter = Utils::String::unquote(contentType.midRef(idx + boundaryFieldName.size())).toLatin1();
if (delimiter.isEmpty()) {
qWarning() << Q_FUNC_INFO << "boundary delimiter field empty!";
return false;
}
qDebug() << "Boundary is " << boundary;
QList<QByteArray> parts = splitMultipartData(data, boundary);
qDebug() << parts.size() << "parts in data";
// split data by "dash-boundary"
const QByteArray dashDelimiter = QByteArray("--") + delimiter + CRLF;
QList<QByteArray> multipart = splitToViews(data, dashDelimiter, QString::SkipEmptyParts);
if (multipart.isEmpty()) {
qWarning() << Q_FUNC_INFO << "multipart empty";
return false;
}
// remove the ending delimiter
const QByteArray endDelimiter = QByteArray("--") + delimiter + QByteArray("--") + CRLF;
multipart.push_back(viewWithoutEndingWith(multipart.takeLast(), endDelimiter));
for (const auto &part : multipart) {
foreach (const QByteArray& part, parts) {
if (!parseFormData(part))
return false;
}
@@ -253,60 +273,71 @@ bool RequestParser::parsePostMessage(const QByteArray &data)
return true;
}
qWarning() << Q_FUNC_INFO << "unknown content type:" << contentType;
qWarning() << Q_FUNC_INFO << "unknown content type:" << qPrintable(contentType);
return false;
}
bool RequestParser::parseFormData(const QByteArray &data)
{
const QList<QByteArray> list = splitToViews(data, EOH, QString::KeepEmptyParts);
if (list.size() != 2) {
qWarning() << Q_FUNC_INFO << "multipart/form-data format error";
// Parse form data header
const int headerEnd = data.indexOf(EOH);
if (headerEnd < 0) {
qDebug() << "Invalid form data: \n" << data;
return false;
}
const QString headers = QString::fromLatin1(list[0]);
const QByteArray payload = viewWithoutEndingWith(list[1], CRLF);
QString headerStr = QString::fromUtf8(data.left(headerEnd));
QStringList lines = headerStr.trimmed().split(EOL);
QStringMap headers;
foreach (const QString& line, lines) {
QPair<QString, QString> header;
if (!parseHeaderLine(line, header))
return false;
QStringMap headersMap;
const QVector<QStringRef> headerLines = headers.splitRef(CRLF, QString::SkipEmptyParts);
for (const auto &line : headerLines) {
if (line.trimmed().startsWith(HEADER_CONTENT_DISPOSITION, Qt::CaseInsensitive)) {
// extract out filename & name
const QVector<QStringRef> directives = line.split(';', QString::SkipEmptyParts);
for (const auto &directive : directives) {
const int idx = directive.indexOf('=');
if (idx < 0)
continue;
const QString name = directive.left(idx).trimmed().toString().toLower();
const QString value = Utils::String::unquote(directive.mid(idx + 1).trimmed()).toString();
headersMap[name] = value;
}
}
else {
if (!parseHeaderLine(line.toString(), headersMap))
return false;
}
headers[header.first] = header.second;
}
// pick data
const QLatin1String filename("filename");
const QLatin1String name("name");
if (headersMap.contains(filename)) {
m_request.files.append({headersMap[filename], headersMap[HEADER_CONTENT_TYPE], payload});
QStringMap disposition;
if (!headers.contains("content-disposition")
|| !parseHeaderValue(headers["content-disposition"], disposition)
|| !disposition.contains("name")) {
qDebug() << "Invalid form data header: \n" << headerStr;
return false;
}
else if (headersMap.contains(name)) {
m_request.posts[headersMap[name]] = payload;
if (disposition.contains("filename")) {
UploadedFile ufile;
ufile.filename = disposition["filename"];
ufile.type = disposition["content-type"];
ufile.data = data.mid(headerEnd + EOH.length());
m_request.files.append(ufile);
}
else {
// malformed
qWarning() << Q_FUNC_INFO << "multipart/form-data header error";
return false;
m_request.posts[disposition["name"]] = QString::fromUtf8(data.mid(headerEnd + EOH.length()));
}
return true;
}
bool RequestParser::parseHeaderValue(const QString &value, QStringMap &out)
{
int pos = value.indexOf(QLatin1Char(';'));
if (pos == -1) {
out[""] = value.trimmed();
return true;
}
out[""] = value.left(pos).trimmed();
QRegExp rx(";\\s*([^=;\"]+)\\s*=\\s*(\"[^\"]*\"|[^\";\\s]+)\\s*");
while (rx.indexIn(value, pos) == pos) {
out[rx.cap(1).trimmed()] = unquoted(rx.cap(2));
pos += rx.cap(0).length();
}
if (pos != value.length())
return false;
return true;
}

View File

@@ -1,6 +1,5 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Mike Tzou (Chocobo1)
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
*
@@ -38,35 +37,32 @@ namespace Http
class RequestParser
{
public:
enum class ParseStatus
enum ErrorCode
{
OK,
Incomplete,
NoError = 0,
IncompleteRequest,
BadRequest
};
struct ParseResult
{
// when `status != ParseStatus::OK`, `request` & `frameSize` are undefined
ParseStatus status;
Request request;
long frameSize; // http request frame size (bytes)
};
static ParseResult parse(const QByteArray &data);
static const long MAX_CONTENT_SIZE = 64 * 1024 * 1024; // 64 MB
// when result != NoError parsed request is undefined
// Warning! Header names are converted to lower-case.
static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */);
private:
RequestParser();
RequestParser(uint maxContentLength);
ParseResult doParse(const QByteArray &data);
bool parseStartLines(const QString &data);
bool parseRequestLine(const QString &line);
ErrorCode parseHttpRequest(const QByteArray &data, Request &request);
bool parsePostMessage(const QByteArray &data);
bool parseHttpHeader(const QByteArray &data);
bool parseStartingLine(const QString &line);
bool parseContent(const QByteArray &data);
bool parseFormData(const QByteArray &data);
QList<QByteArray> splitMultipartData(const QByteArray &data, const QByteArray &boundary);
static bool parseHeaderLine(const QString &line, QPair<QString, QString> &out);
static bool parseHeaderValue(const QString &value, QStringMap &out);
const uint m_maxContentLength;
Request m_request;
};
}

View File

@@ -30,6 +30,11 @@
using namespace Http;
ResponseBuilder::ResponseBuilder(QObject *parent)
: QObject(parent)
{
}
void ResponseBuilder::status(uint code, const QString &text)
{
m_response.status = ResponseStatus(code, text);

View File

@@ -29,13 +29,17 @@
#ifndef HTTP_RESPONSEBUILDER_H
#define HTTP_RESPONSEBUILDER_H
#include <QObject>
#include "types.h"
namespace Http
{
class ResponseBuilder
class ResponseBuilder : public QObject
{
public:
explicit ResponseBuilder(QObject *parent = nullptr);
protected:
void status(uint code = 200, const QString &text = QLatin1String("OK"));
void header(const QString &name, const QString &value);
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Mike Tzou (Chocobo1)
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
@@ -38,12 +37,7 @@
namespace Http
{
const char METHOD_GET[] = "GET";
const char METHOD_POST[] = "POST";
const char HEADER_CACHE_CONTROL[] = "cache-control";
const char HEADER_CONNECTION[] = "connection";
const char HEADER_CONTENT_DISPOSITION[] = "content-disposition";
const char HEADER_CONTENT_ENCODING[] = "content-encoding";
const char HEADER_CONTENT_LENGTH[] = "content-length";
const char HEADER_CONTENT_SECURITY_POLICY[] = "content-security-policy";
@@ -52,28 +46,22 @@ 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";
const char HEADER_X_FRAME_OPTIONS[] = "x-frame-options";
const char HEADER_X_XSS_PROTECTION[] = "x-xss-protection";
const char HEADER_REQUEST_METHOD_GET[] = "GET";
const char HEADER_REQUEST_METHOD_HEAD[] = "HEAD";
const char HEADER_REQUEST_METHOD_POST[] = "POST";
const char CONTENT_TYPE_HTML[] = "text/html";
const char CONTENT_TYPE_CSS[] = "text/css";
const char CONTENT_TYPE_TXT[] = "text/plain";
const char CONTENT_TYPE_JS[] = "application/javascript";
const char CONTENT_TYPE_JSON[] = "application/json";
const char CONTENT_TYPE_CSS[] = "text/css; charset=UTF-8";
const char CONTENT_TYPE_GIF[] = "image/gif";
const char CONTENT_TYPE_HTML[] = "text/html; charset=UTF-8";
const char CONTENT_TYPE_JS[] = "application/javascript; charset=UTF-8";
const char CONTENT_TYPE_JSON[] = "application/json";
const char CONTENT_TYPE_PNG[] = "image/png";
const char CONTENT_TYPE_FORM_ENCODED[] = "application/x-www-form-urlencoded";
const char CONTENT_TYPE_FORM_DATA[] = "multipart/form-data";
const char CONTENT_TYPE_TXT[] = "text/plain; charset=UTF-8";
const char CONTENT_TYPE_SVG[] = "image/svg+xml";
// portability: "\r\n" doesn't guarantee mapping to the correct symbol
// portability: "\r\n" doesn't guarantee mapping to the correct value
const char CRLF[] = {0x0D, 0x0A, '\0'};
struct Environment
@@ -87,18 +75,17 @@ namespace Http
struct UploadedFile
{
QString filename;
QString type; // MIME type
QByteArray data;
QString filename; // original filename
QString type; // MIME type
QByteArray data; // File data
};
struct Request
{
QString version;
QString method;
QString path;
QByteArray query;
QStringMap headers;
QStringMap gets;
QStringMap posts;
QVector<UploadedFile> files;
};
@@ -108,7 +95,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
@@ -117,7 +104,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

@@ -27,10 +27,9 @@
* exception statement from your version.
*/
#include <QString>
#include "iconprovider.h"
#include <QFileInfo>
IconProvider::IconProvider(QObject *parent)
: QObject(parent)
{
@@ -48,7 +47,7 @@ void IconProvider::freeInstance()
{
if (m_instance) {
delete m_instance;
m_instance = nullptr;
m_instance = 0;
}
}
@@ -57,15 +56,9 @@ IconProvider *IconProvider::instance()
return m_instance;
}
QString IconProvider::getIconPath(const QString &iconId) const
QString IconProvider::getIconPath(const QString &iconId)
{
// there are a few icons not available in svg
const QString pathSvg = ":/icons/qbt-theme/" + iconId + ".svg";
if (QFileInfo::exists(pathSvg))
return pathSvg;
const QString pathPng = ":/icons/qbt-theme/" + iconId + ".png";
return pathPng;
return ":/icons/qbt-theme/" + iconId + ".png";
}
IconProvider *IconProvider::m_instance = nullptr;
IconProvider *IconProvider::m_instance = 0;

View File

@@ -31,7 +31,8 @@
#define ICONPROVIDER_H
#include <QObject>
#include <QString>
class QString;
class IconProvider : public QObject
{
@@ -42,10 +43,10 @@ public:
static void freeInstance();
static IconProvider *instance();
virtual QString getIconPath(const QString &iconId) const;
virtual QString getIconPath(const QString &iconId);
protected:
explicit IconProvider(QObject *parent = nullptr);
explicit IconProvider(QObject *parent = 0);
~IconProvider();
static IconProvider *m_instance;

View File

@@ -38,30 +38,30 @@ class IndexInterval
public:
using IndexType = Index;
IndexInterval(IndexType first, IndexType last) // add constexpr when using C++14
IndexInterval(IndexType first, IndexType last)
: m_first {first}
, m_last {last}
{
Q_ASSERT(first <= last);
}
constexpr IndexType first() const
IndexType first() const
{
return m_first;
}
constexpr IndexType last() const
IndexType last() const
{
return m_last;
}
private:
const IndexType m_first;
const IndexType m_last;
IndexType m_first;
IndexType m_last;
};
template <typename T>
constexpr IndexInterval<T> makeInterval(T first, T last)
inline IndexInterval<T> makeInterval(T first, T last)
{
return {first, last};
}
@@ -99,7 +99,7 @@ public:
constexpr IndexType end() const
{
return (m_first + m_size);
return m_first + m_size;
}
constexpr IndexDiffType size() const
@@ -114,12 +114,12 @@ public:
constexpr IndexType last() const
{
return (m_first + m_size - 1);
return m_first + m_size - 1;
}
constexpr bool isEmpty() const
{
return (m_size == 0);
return m_size == 0;
}
private:

View File

@@ -3,12 +3,12 @@
#include <QDateTime>
#include "base/utils/string.h"
Logger *Logger::m_instance = nullptr;
Logger* Logger::m_instance = 0;
Logger::Logger()
: m_lock(QReadWriteLock::Recursive)
, m_msgCounter(0)
, m_peerCounter(0)
: lock(QReadWriteLock::Recursive)
, msgCounter(0)
, peerCounter(0)
{
}
@@ -29,15 +29,15 @@ void Logger::freeInstance()
{
if (m_instance) {
delete m_instance;
m_instance = nullptr;
m_instance = 0;
}
}
void Logger::addMessage(const QString &message, const Log::MsgType &type)
{
QWriteLocker locker(&m_lock);
QWriteLocker locker(&lock);
Log::Msg temp = {m_msgCounter++, QDateTime::currentMSecsSinceEpoch(), type, message.toHtmlEscaped()};
Log::Msg temp = { msgCounter++, QDateTime::currentMSecsSinceEpoch(), type, message.toHtmlEscaped() };
m_messages.push_back(temp);
if (m_messages.size() >= MAX_LOG_MESSAGES)
@@ -48,9 +48,9 @@ void Logger::addMessage(const QString &message, const Log::MsgType &type)
void Logger::addPeer(const QString &ip, bool blocked, const QString &reason)
{
QWriteLocker locker(&m_lock);
QWriteLocker locker(&lock);
Log::Peer temp = {m_peerCounter++, QDateTime::currentMSecsSinceEpoch(), ip.toHtmlEscaped(), blocked, reason.toHtmlEscaped()};
Log::Peer temp = { peerCounter++, QDateTime::currentMSecsSinceEpoch(), ip.toHtmlEscaped(), blocked, reason.toHtmlEscaped() };
m_peers.push_back(temp);
if (m_peers.size() >= MAX_LOG_MESSAGES)
@@ -61,9 +61,9 @@ void Logger::addPeer(const QString &ip, bool blocked, const QString &reason)
QVector<Log::Msg> Logger::getMessages(int lastKnownId) const
{
QReadLocker locker(&m_lock);
QReadLocker locker(&lock);
int diff = m_msgCounter - lastKnownId - 1;
int diff = msgCounter - lastKnownId - 1;
int size = m_messages.size();
if ((lastKnownId == -1) || (diff >= size))
@@ -77,9 +77,9 @@ QVector<Log::Msg> Logger::getMessages(int lastKnownId) const
QVector<Log::Peer> Logger::getPeers(int lastKnownId) const
{
QReadLocker locker(&m_lock);
QReadLocker locker(&lock);
int diff = m_peerCounter - lastKnownId - 1;
int diff = peerCounter - lastKnownId - 1;
int size = m_peers.size();
if ((lastKnownId == -1) || (diff >= size))

View File

@@ -1,10 +1,10 @@
#ifndef LOGGER_H
#define LOGGER_H
#include <QObject>
#include <QReadWriteLock>
#include <QString>
#include <QVector>
#include <QReadWriteLock>
#include <QObject>
const int MAX_LOG_MESSAGES = 20000;
@@ -16,7 +16,7 @@ namespace Log
NORMAL = 0x1,
INFO = 0x2,
WARNING = 0x4,
CRITICAL = 0x8 // ERROR is defined by libtorrent and results in compiler error
CRITICAL = 0x8 //ERROR is defined by libtorrent and results in compiler error
};
Q_DECLARE_FLAGS(MsgTypes, MsgType)
@@ -63,12 +63,12 @@ private:
Logger();
~Logger();
static Logger *m_instance;
static Logger* m_instance;
QVector<Log::Msg> m_messages;
QVector<Log::Peer> m_peers;
mutable QReadWriteLock m_lock;
int m_msgCounter;
int m_peerCounter;
mutable QReadWriteLock lock;
int msgCounter;
int peerCounter;
};
// Helper function

View File

@@ -26,15 +26,15 @@
* exception statement from your version.
*/
#include "dnsupdater.h"
#include <QDebug>
#include <QRegularExpression>
#include <QRegExp>
#include <QStringList>
#include <QUrlQuery>
#include "base/logger.h"
#include "base/net/downloadhandler.h"
#include "base/net/downloadmanager.h"
#include "dnsupdater.h"
using namespace Net;
@@ -52,7 +52,7 @@ DNSUpdater::DNSUpdater(QObject *parent)
// Start IP checking timer
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
connect(&m_ipCheckTimer, &QTimer::timeout, this, &DNSUpdater::checkPublicIP);
connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP()));
m_ipCheckTimer.start();
// Check lastUpdate to avoid flooding
@@ -74,11 +74,11 @@ void DNSUpdater::checkPublicIP()
{
Q_ASSERT(m_state == OK);
DownloadHandler *handler = DownloadManager::instance()->download(
DownloadRequest("http://checkip.dyndns.org").userAgent("qBittorrent/" QBT_VERSION_2));
connect(handler, static_cast<void (Net::DownloadHandler::*)(const QString &, const QByteArray &)>(&Net::DownloadHandler::downloadFinished)
, this, &DNSUpdater::ipRequestFinished);
connect(handler, &Net::DownloadHandler::downloadFailed, this, &DNSUpdater::ipRequestFailed);
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(
"http://checkip.dyndns.org", false, 0, false,
"qBittorrent/" QBT_VERSION_2);
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(ipRequestFinished(QString, QByteArray)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(ipRequestFailed(QString, QString)));
m_lastIPCheckTime = QDateTime::currentDateTime();
}
@@ -88,9 +88,9 @@ void DNSUpdater::ipRequestFinished(const QString &url, const QByteArray &data)
Q_UNUSED(url);
// Parse response
const QRegularExpressionMatch ipRegexMatch = QRegularExpression("Current IP Address:\\s+([^<]+)</body>").match(data);
if (ipRegexMatch.hasMatch()) {
QString ipStr = ipRegexMatch.captured(1);
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
if (ipregex.indexIn(data) >= 0) {
QString ipStr = ipregex.cap(1);
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ipStr;
QHostAddress newIp(ipStr);
if (!newIp.isNull()) {
@@ -121,11 +121,11 @@ void DNSUpdater::updateDNSService()
qDebug() << Q_FUNC_INFO;
m_lastIPCheckTime = QDateTime::currentDateTime();
DownloadHandler *handler = DownloadManager::instance()->download(
DownloadRequest(getUpdateUrl()).userAgent("qBittorrent/" QBT_VERSION_2));
connect(handler, static_cast<void (Net::DownloadHandler::*)(const QString &, const QByteArray &)>(&Net::DownloadHandler::downloadFinished)
, this, &DNSUpdater::ipUpdateFinished);
connect(handler, &Net::DownloadHandler::downloadFailed, this, &DNSUpdater::ipUpdateFailed);
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(
getUpdateUrl(), false, 0, false,
"qBittorrent/" QBT_VERSION_2);
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(ipUpdateFinished(QString, QByteArray)));
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(ipUpdateFailed(QString, QString)));
}
QString DNSUpdater::getUpdateUrl() const
@@ -181,7 +181,7 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
{
Logger *const logger = Logger::instance();
qDebug() << Q_FUNC_INFO << reply;
QString code = reply.split(' ').first();
QString code = reply.split(" ").first();
qDebug() << Q_FUNC_INFO << "Code:" << code;
if ((code == "good") || (code == "nochg")) {
@@ -196,7 +196,7 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
return;
}
// Everything below is an error, stop updating until the user updates something
// Everything bellow is an error, stop updating until the user updates something
m_ipCheckTimer.stop();
m_lastIP.clear();
if (code == "nohost") {
@@ -244,8 +244,8 @@ void DNSUpdater::updateCredentials()
}
if (m_domain != pref->getDynDomainName()) {
m_domain = pref->getDynDomainName();
const QRegularExpressionMatch domainRegexMatch = QRegularExpression("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$").match(m_domain);
if (!domainRegexMatch.hasMatch()) {
QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$");
if (domain_regex.indexIn(m_domain) < 0) {
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
m_lastIP.clear();
m_ipCheckTimer.stop();

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@@ -29,11 +29,11 @@
#include "downloadhandler.h"
#include <QCoreApplication>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkCookie>
#include <QNetworkProxy>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QTemporaryFile>
#include <QUrl>
@@ -43,57 +43,35 @@
#include "base/utils/misc.h"
#include "downloadmanager.h"
namespace
{
bool saveToFile(const QByteArray &replyData, QString &filePath)
{
QTemporaryFile tmpfile {Utils::Fs::tempPath() + "XXXXXX"};
tmpfile.setAutoRemove(false);
static QString errorCodeToString(QNetworkReply::NetworkError status);
if (!tmpfile.open())
return false;
using namespace Net;
filePath = tmpfile.fileName();
tmpfile.write(replyData);
return true;
}
}
Net::DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, const DownloadRequest &downloadRequest)
DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile, qint64 limit, bool handleRedirectToMagnet)
: QObject(manager)
, m_reply(reply)
, m_manager(manager)
, m_downloadRequest(downloadRequest)
, m_saveToFile(saveToFile)
, m_sizeLimit(limit)
, m_handleRedirectToMagnet(handleRedirectToMagnet)
, m_url(reply->url().toString())
{
if (reply)
assignNetworkReply(reply);
init();
}
Net::DownloadHandler::~DownloadHandler()
DownloadHandler::~DownloadHandler()
{
if (m_reply)
delete m_reply;
}
void Net::DownloadHandler::assignNetworkReply(QNetworkReply *reply)
{
Q_ASSERT(reply);
m_reply = reply;
m_reply->setParent(this);
if (m_downloadRequest.limit() > 0)
connect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize);
connect(m_reply, &QNetworkReply::finished, this, &Net::DownloadHandler::processFinishedDownload);
}
// Returns original url
QString Net::DownloadHandler::url() const
QString DownloadHandler::url() const
{
return m_downloadRequest.url();
return m_url;
}
void Net::DownloadHandler::processFinishedDownload()
void DownloadHandler::processFinishedDownload()
{
QString url = m_reply->url().toString();
qDebug("Download finished: %s", qUtf8Printable(url));
@@ -101,7 +79,7 @@ void Net::DownloadHandler::processFinishedDownload()
if (m_reply->error() != QNetworkReply::NoError) {
// Failure
qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error())));
emit downloadFailed(m_downloadRequest.url(), errorCodeToString(m_reply->error()));
emit downloadFailed(m_url, errorCodeToString(m_reply->error()));
this->deleteLater();
}
else {
@@ -119,15 +97,15 @@ void Net::DownloadHandler::processFinishedDownload()
replyData = Utils::Gzip::decompress(replyData);
}
if (m_downloadRequest.saveToFile()) {
if (m_saveToFile) {
QString filePath;
if (saveToFile(replyData, filePath))
emit downloadFinished(m_downloadRequest.url(), filePath);
emit downloadFinished(m_url, filePath);
else
emit downloadFailed(m_downloadRequest.url(), tr("I/O Error"));
}
emit downloadFailed(m_url, tr("I/O Error"));
}
else {
emit downloadFinished(m_downloadRequest.url(), replyData);
emit downloadFinished(m_url, replyData);
}
this->deleteLater();
@@ -135,116 +113,137 @@ void Net::DownloadHandler::processFinishedDownload()
}
}
void Net::DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal)
void DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal)
{
QString msg = tr("The file size is %1. It exceeds the download limit of %2.");
if (bytesTotal > 0) {
// Total number of bytes is available
if (bytesTotal > m_downloadRequest.limit()) {
if (bytesTotal > m_sizeLimit) {
m_reply->abort();
emit downloadFailed(m_downloadRequest.url(), msg.arg(Utils::Misc::friendlyUnit(bytesTotal), Utils::Misc::friendlyUnit(m_downloadRequest.limit())));
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesTotal)).arg(Utils::Misc::friendlyUnit(m_sizeLimit)));
}
else {
disconnect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize);
disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64)));
}
}
else if (bytesReceived > m_downloadRequest.limit()) {
else if (bytesReceived > m_sizeLimit) {
m_reply->abort();
emit downloadFailed(m_downloadRequest.url(), msg.arg(Utils::Misc::friendlyUnit(bytesReceived), Utils::Misc::friendlyUnit(m_downloadRequest.limit())));
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesReceived)).arg(Utils::Misc::friendlyUnit(m_sizeLimit)));
}
}
void Net::DownloadHandler::handleRedirection(QUrl newUrl)
void DownloadHandler::init()
{
m_reply->setParent(this);
if (m_sizeLimit > 0)
connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64)));
connect(m_reply, SIGNAL(finished()), this, SLOT(processFinishedDownload()));
}
bool DownloadHandler::saveToFile(const QByteArray &replyData, QString &filePath)
{
QTemporaryFile *tmpfile = new QTemporaryFile(Utils::Fs::tempPath() + "XXXXXX");
if (!tmpfile->open()) {
delete tmpfile;
return false;
}
tmpfile->setAutoRemove(false);
filePath = tmpfile->fileName();
qDebug("Temporary filename is: %s", qUtf8Printable(filePath));
if (m_reply->isOpen() || m_reply->open(QIODevice::ReadOnly)) {
tmpfile->write(replyData);
tmpfile->close();
// XXX: tmpfile needs to be deleted on Windows before using the file
// or it will complain that the file is used by another process.
delete tmpfile;
return true;
}
else {
delete tmpfile;
Utils::Fs::forceRemove(filePath);
}
return false;
}
void DownloadHandler::handleRedirection(QUrl newUrl)
{
// Resolve relative urls
if (newUrl.isRelative())
newUrl = m_reply->url().resolved(newUrl);
const QString newUrlString = newUrl.toString();
qDebug("Redirecting from %s to %s...", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString));
qDebug("Redirecting from %s to %s", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString));
// Redirect to magnet workaround
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) {
qDebug("Magnet redirect detected.");
m_reply->abort();
if (m_downloadRequest.handleRedirectToMagnet())
emit redirectedToMagnet(m_downloadRequest.url(), newUrlString);
if (m_handleRedirectToMagnet)
emit redirectedToMagnet(m_url, newUrlString);
else
emit downloadFailed(m_downloadRequest.url(), tr("Unexpected redirect to magnet URI."));
emit downloadFailed(m_url, tr("Unexpected redirect to magnet URI."));
this->deleteLater();
}
else {
DownloadHandler *redirected = m_manager->download(DownloadRequest(m_downloadRequest).url(newUrlString));
connect(redirected, &DownloadHandler::destroyed, this, &DownloadHandler::deleteLater);
connect(redirected, &DownloadHandler::downloadFailed, this, [this](const QString &, const QString &reason)
{
emit downloadFailed(url(), reason);
});
connect(redirected, &DownloadHandler::redirectedToMagnet, this, [this](const QString &, const QString &magnetUri)
{
emit redirectedToMagnet(url(), magnetUri);
});
connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QString &)>(&DownloadHandler::downloadFinished)
, this, [this](const QString &, const QString &fileName)
{
emit downloadFinished(url(), fileName);
});
connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QByteArray &)>(&DownloadHandler::downloadFinished)
, this, [this](const QString &, const QByteArray &data)
{
emit downloadFinished(url(), data);
});
DownloadHandler *tmp = m_manager->downloadUrl(newUrlString, m_saveToFile, m_sizeLimit, m_handleRedirectToMagnet);
m_reply->deleteLater();
m_reply = tmp->m_reply;
init();
tmp->m_reply = nullptr;
delete tmp;
}
}
QString Net::DownloadHandler::errorCodeToString(const QNetworkReply::NetworkError status)
QString errorCodeToString(QNetworkReply::NetworkError status)
{
switch (status) {
case QNetworkReply::HostNotFoundError:
return tr("The remote host name was not found (invalid hostname)");
return QObject::tr("The remote host name was not found (invalid hostname)");
case QNetworkReply::OperationCanceledError:
return tr("The operation was canceled");
return QObject::tr("The operation was canceled");
case QNetworkReply::RemoteHostClosedError:
return tr("The remote server closed the connection prematurely, before the entire reply was received and processed");
return QObject::tr("The remote server closed the connection prematurely, before the entire reply was received and processed");
case QNetworkReply::TimeoutError:
return tr("The connection to the remote server timed out");
return QObject::tr("The connection to the remote server timed out");
case QNetworkReply::SslHandshakeFailedError:
return tr("SSL/TLS handshake failed");
return QObject::tr("SSL/TLS handshake failed");
case QNetworkReply::ConnectionRefusedError:
return tr("The remote server refused the connection");
return QObject::tr("The remote server refused the connection");
case QNetworkReply::ProxyConnectionRefusedError:
return tr("The connection to the proxy server was refused");
return QObject::tr("The connection to the proxy server was refused");
case QNetworkReply::ProxyConnectionClosedError:
return tr("The proxy server closed the connection prematurely");
return QObject::tr("The proxy server closed the connection prematurely");
case QNetworkReply::ProxyNotFoundError:
return tr("The proxy host name was not found");
return QObject::tr("The proxy host name was not found");
case QNetworkReply::ProxyTimeoutError:
return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent");
return QObject::tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent");
case QNetworkReply::ProxyAuthenticationRequiredError:
return tr("The proxy requires authentication in order to honor the request but did not accept any credentials offered");
return QObject::tr("The proxy requires authentication in order to honor the request but did not accept any credentials offered");
case QNetworkReply::ContentAccessDenied:
return tr("The access to the remote content was denied (401)");
return QObject::tr("The access to the remote content was denied (401)");
case QNetworkReply::ContentOperationNotPermittedError:
return tr("The operation requested on the remote content is not permitted");
return QObject::tr("The operation requested on the remote content is not permitted");
case QNetworkReply::ContentNotFoundError:
return tr("The remote content was not found at the server (404)");
return QObject::tr("The remote content was not found at the server (404)");
case QNetworkReply::AuthenticationRequiredError:
return tr("The remote server requires authentication to serve the content but the credentials provided were not accepted");
return QObject::tr("The remote server requires authentication to serve the content but the credentials provided were not accepted");
case QNetworkReply::ProtocolUnknownError:
return tr("The Network Access API cannot honor the request because the protocol is not known");
return QObject::tr("The Network Access API cannot honor the request because the protocol is not known");
case QNetworkReply::ProtocolInvalidOperationError:
return tr("The requested operation is invalid for this protocol");
return QObject::tr("The requested operation is invalid for this protocol");
case QNetworkReply::UnknownNetworkError:
return tr("An unknown network-related error was detected");
return QObject::tr("An unknown network-related error was detected");
case QNetworkReply::UnknownProxyError:
return tr("An unknown proxy-related error was detected");
return QObject::tr("An unknown proxy-related error was detected");
case QNetworkReply::UnknownContentError:
return tr("An unknown error related to the remote content was detected");
return QObject::tr("An unknown error related to the remote content was detected");
case QNetworkReply::ProtocolFailure:
return tr("A breakdown in protocol was detected");
return QObject::tr("A breakdown in protocol was detected");
default:
return tr("Unknown error");
return QObject::tr("Unknown error");
}
}

View File

@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@@ -30,11 +30,10 @@
#ifndef NET_DOWNLOADHANDLER_H
#define NET_DOWNLOADHANDLER_H
#include <QNetworkReply>
#include <QObject>
#include "downloadmanager.h"
class QNetworkAccessManager;
class QNetworkReply;
class QUrl;
namespace Net
@@ -44,14 +43,10 @@ namespace Net
class DownloadHandler : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(DownloadHandler)
friend class DownloadManager;
DownloadHandler(QNetworkReply *reply, DownloadManager *manager, const DownloadRequest &downloadRequest);
public:
~DownloadHandler() override;
DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false);
~DownloadHandler();
QString url() const;
@@ -66,14 +61,16 @@ namespace Net
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
private:
void assignNetworkReply(QNetworkReply *reply);
void init();
bool saveToFile(const QByteArray &replyData, QString &filePath);
void handleRedirection(QUrl newUrl);
static QString errorCodeToString(QNetworkReply::NetworkError status);
QNetworkReply *m_reply;
DownloadManager *m_manager;
const DownloadRequest m_downloadRequest;
bool m_saveToFile;
qint64 m_sizeLimit;
bool m_handleRedirectToMagnet;
QString m_url;
};
}

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